{
 *****************************************************************************
 *                             Gtk3CellRenderer.pas                          *
 *                             --------------------                          *
 *                                                                           *
 *                                                                           *
 *****************************************************************************

 *****************************************************************************
  This file is part of the Lazarus Component Library (LCL)

  See the file COPYING.modifiedLGPL.txt, included in this distribution,
  for details about the license.
 *****************************************************************************

  An extended gtk_cell_renderer, to provide hooks for the LCL.
  For example for custom drawing.
 
}
unit Gtk3CellRenderer;

{$mode objfpc}{$H+}
{$i gtk3defines.inc}

interface

uses
  Classes, SysUtils, LCLType, LCLProc, Controls, StdCtrls, ComCtrls, LMessages,
  LazGObject2, LazGtk3, LazGdk3, LazGLib2, Gtk3Procs, LazCairo1;
  
type
  PLCLIntfCellRenderer = ^TLCLIntfCellRenderer;
  TLCLIntfCellRenderer = record
    // ! the TextRenderer must be the first attribute of this record !
    TextRenderer: TGtkCellRendererText;
    Index: integer;
    ColumnIndex: Integer; // for TListView
  end;

  PLCLIntfCellRendererClass = ^TLCLIntfCellRendererClass;
  TLCLIntfCellRendererClass = object
    // ParentClass: TGInitiallyUnowned;
    ParentClass: TGtkCellRendererTextClass;
    DefaultGetRequestMode: function(cell: PGtkCellRenderer): TGtkSizeRequestMode; cdecl;
    DefaultGetPreferredWidth: procedure (cell: PGtkCellRenderer; widget: PGtkWidget; minimum_size: Pgint; natural_size: Pgint); cdecl;
    DefaultGetPreferredHeightForWidth: procedure(cell: PGtkCellRenderer; widget: PGtkWidget; width: gint; minimum_height: Pgint; natural_height: Pgint); cdecl;
    DefaultGetPreferredHeight: procedure (cell: PGtkCellRenderer; widget: PGtkWidget; minimum_size: Pgint; natural_size: Pgint); cdecl;
    DefaultGetPreferredWidthForHeight: procedure(cell: PGtkCellRenderer; widget: PGtkWidget; height: gint; minimum_width: Pgint; natural_width: Pgint); cdecl;
    DefaultGetAlignedArea: procedure(cell: PGtkCellRenderer; widget: PGtkWidget; flags: TGtkCellRendererState; cell_area: PGdkRectangle; aligned_area: PGdkRectangle); cdecl;

    DefaultGtkGetSize: procedure(cell: PGtkCellRenderer;
                                 widget: PGtkWidget;
                                 cell_area: PGdkRectangle;
                                 x_offset: pgint;
                                 y_offset: pgint;
                                 width: pgint;
                                 height: pgint); cdecl;

    DefaultGtkRender: procedure(cell: PGtkCellRenderer;
              cr: Pcairo_t;
              widget: PGtkWidget;
              background_area: PGdkRectangle;
              cell_area: PGdkRectangle;
              flags: TGtkCellRendererState); cdecl;

    activate: function (cell: PGtkCellRenderer; event: PGdkEvent; widget: PGtkWidget;
      path: PgChar; bg_area: PGdkRectangle; cell_area: PGdkRectangle;
      flags: TGtkCellRendererState): GBoolean; cdecl;

    start_editing: function(cell: PGtkCellRenderer; event: PGdkEvent; widget: PGtkWidget;
      path: PgChar; bg_area: PGdkRectangle; cell_area: PGdkRectangle; flags: TGtkCellRendererState): PGtkCellEditable; cdecl;
    editing_canceled: procedure(cell: PGtkCellRenderer); cdecl;
    editing_started: procedure(cell: PGtkCellRenderer; editable: PGtkCellEditable; path: pgChar); cdecl;
    priv: PGtkCellRendererClassPrivate;
    _gtk_reserved2: procedure(); cdecl;
    _gtk_reserved3: procedure(); cdecl;
    _gtk_reserved4: procedure(); cdecl;
  end;

function LCLIntfCellRenderer_GetType: TGType;
function LCLIntfCellRenderer_New: PGtkCellRenderer;
procedure LCLIntfCellRenderer_CellDataFunc(cell_layout:PGtkCellLayout;
                                           cell: PGtkCellRenderer;
                                           tree_model: PGtkTreeModel;
                                           iter: PGtkTreeIter;
                                           data: gpointer); cdecl;
procedure LCLIntfRenderer_ColumnCellDataFunc(tree_column: PGtkTreeViewColumn;
                                           cell: PGtkCellRenderer;
                                           tree_model: PGtkTreeModel;
                                           iter: PGtkTreeIter;
                                           data: gpointer); cdecl;
procedure LCLIntfRenderer_GtkCellLayoutDataFunc(cell_layout: PGtkCellLayout; cell: PGtkCellRenderer; tree_model: PGtkTreeModel; iter: PGtkTreeIter; data: gpointer); cdecl;

implementation
uses gtk3widgets, gtk3int;

type
  TCustomListViewAccess = class(TCustomListView);

function GetControl(cell: PGtkCellRenderer; Widget: PGtkWidget): TWinControl;
var
  AHWND: HWND;
  AWidget: PGtkWidget;
begin
  Result := nil;
  AHWND := HwndFromGtkWidget(Widget);
  if AHWND = 0 then
  begin
    AWidget := Widget^.get_parent;
    while (AWidget <> nil) do
    begin
      AHWND := HwndFromGtkWidget(AWidget);
      if AHWND <> 0 then
        break;
      AWidget := AWidget^.get_parent;
    end;
  end;
  if AHWND <> 0 then
    Result := TGtk3Widget(AHWND).LCLObject;
end;

function GetItemIndex(cell: PLCLIntfCellRenderer; {%H-}widget: PGtkWidget): Integer;
begin
  Result := cell^.Index;
end;

function LCLIntfCellRenderer_GetRequestMode(cell: PGtkCellRenderer): TGtkSizeRequestMode; cdecl;
var
  CellClass: PLCLIntfCellRendererClass;
begin
  {$IFDEF GTK3DEBUGCELLRENDERER}
  DebugLn('*** LCLIntfCellRenderer_GetRequestMode ***');
  {$ENDIF}
  CellClass := PLCLIntfCellRendererClass(cell^.g_type_instance.g_class);
  // CellClass := PLCLIntfCellRendererClass(gtk_object_get_class(cell));
  Result := CellClass^.DefaultGetRequestMode(cell);
end;

procedure LCLIntfCellRenderer_GetAlignedArea(cell: PGtkCellRenderer; widget: PGtkWidget;
  flags: TGtkCellRendererState; cell_area: PGdkRectangle; aligned_area: PGdkRectangle); cdecl;
var
  CellClass: PLCLIntfCellRendererClass;
  AWinControl: TWinControl;
  ItemIndex: Integer;
  Msg: TLMMeasureItem;
  MeasureItemStruct: TMeasureItemStruct;
  Requisition, NaturalSize: TGtkRequisition;
  AMinHeight, ANaturalHeight: gint;
  Value: TGValue;
  R1, R2: TGdkRectangle;
begin
  {$IFDEF GTK3DEBUGCELLRENDERER}
  // DebugLn('*** LCLIntfCellRenderer_GetAlignedArea ***');
  {$ENDIF}
  CellClass := PLCLIntfCellRendererClass(cell^.g_type_instance.g_class);
  CellClass^.DefaultGetAlignedArea(cell, widget, flags, cell_area, aligned_area);
  // this does not work too....
  exit;

  R1 := cell_area^;
  R2 := aligned_area^;
  CellClass^.DefaultGetAlignedArea(cell, widget, flags, @R1, @R2);
  cell_area^ := R1;
  aligned_area^ := R2;
  // DebugLn('Cell_Area ',dbgs(RectFromGdkRect(cell_area^)),' Aligned_Area ',dbgs(RectFromGdkRect(aligned_area^)));
  // cell^.get_preferred_size(Widget, @AMinSize, @ANatSize);

  AWinControl := GetControl(cell, widget);
  if [csDestroying,csLoading]*AWinControl.ComponentState<>[] then exit;

  if AWinControl is TCustomListbox then
    if TCustomListbox(AWinControl).Style < lbOwnerDrawFixed then
      exit;
  if AWinControl is TCustomCombobox then
    if TCustomCombobox(AWinControl).Style < csOwnerDrawVariable then
      exit;

  ItemIndex := GetItemIndex(PLCLIntfCellRenderer(cell), Widget);

  if ItemIndex < 0 then
    ItemIndex := 0;

  MeasureItemStruct.itemID := UINT(ItemIndex);
  MeasureItemStruct.itemWidth := UINT(aligned_area^.width);
  MeasureItemStruct.itemHeight := UINT(aligned_area^.height);
  Msg.Msg := LM_MEASUREITEM;
  Msg.MeasureItemStruct := @MeasureItemStruct;
  TGtk3Widget(AWinControl.Handle).DeliverMessage(Msg);
  {here we cheat cell renderer to paint eg. height 1 }
  aligned_area^.height := gint(MeasureItemStruct.itemHeight);
  cell_area^.height := gint(MeasureItemStruct.itemHeight);
  DebugLn('**** Cell_Area ',dbgs(RectFromGdkRect(cell_area^)),' Aligned_Area ',dbgs(RectFromGdkRect(aligned_area^)));

end;

// get_preferred_width: procedure(cell: PGtkCellRenderer; widget: PGtkWidget; minimum_size: Pgint; natural_size: Pgint); cdecl;
procedure LCLIntfCellRenderer_GetPreferredWidth(cell: PGtkCellRenderer; widget: PGtkWidget; minimum_size: Pgint; natural_size: Pgint); cdecl;
var
  CellClass: PLCLIntfCellRendererClass;
begin
  {$IFDEF GTK3DEBUGCELLRENDERER}
  DebugLn('*** LCLIntfCellRenderer_GetPreferredWidth ***');
  {$ENDIF}
  CellClass := PLCLIntfCellRendererClass(cell^.g_type_instance.g_class);
  CellClass^.DefaultGetPreferredWidth(cell, widget, minimum_size, natural_size);
  {$IFDEF GTK3DEBUGCELLRENDERER}
  // DebugLn('*** LCLIntfCellRenderer_GetPreferredWidth MIN=',dbgs(minimum_size^),' naturalw=',dbgs(natural_size^));
  {$ENDIF}
end;

procedure LCLIntfCellRenderer_GetPreferredHeight(cell: PGtkCellRenderer; widget: PGtkWidget; minimum_size: Pgint; natural_size: Pgint); cdecl;
var
  CellClass: PLCLIntfCellRendererClass;
begin
  // it's never called ?!?
  {$IFDEF GTK3DEBUGCELLRENDERER}
  DebugLn('*** LCLIntfCellRenderer_GetPreferredHeight ***');
  {$ENDIF}
  CellClass := PLCLIntfCellRendererClass(cell^.g_type_instance.g_class);
  CellClass^.DefaultGetPreferredHeight(cell, widget, minimum_size, natural_size);

end;

procedure LCLIntfCellRenderer_GetPreferredWidthForHeight(cell: PGtkCellRenderer; widget: PGtkWidget;
  height: gint; minimum_width: Pgint; natural_width: Pgint); cdecl;
var
  CellClass: PLCLIntfCellRendererClass;
begin
  {$IFDEF GTK3DEBUGCELLRENDERER}
  DebugLn('*** LCLIntfCellRenderer_GetPreferredWidthForHeight ***');
  {$ENDIF}
  CellClass := PLCLIntfCellRendererClass(cell^.g_type_instance.g_class);
  CellClass^.DefaultGetPreferredWidthForHeight(cell, widget, height, minimum_width, natural_width);
end;

procedure LCLIntfCellRenderer_GetPreferredHeightForWidth(cell: PGtkCellRenderer; widget: PGtkWidget;
  width: gint; minimum_height: Pgint; natural_height: Pgint); cdecl;
var
  CellClass: PLCLIntfCellRendererClass;
  AWinControl: TWinControl;
  ItemIndex: Integer;
  Msg: TLMMeasureItem;
  MeasureItemStruct: TMeasureItemStruct;
  //Requisition, NaturalSize: TGtkRequisition;
  AMinHeight, ANaturalHeight: gint;
  Value: TGValue;
begin
  {$IFDEF GTK3DEBUGCELLRENDERER}
  DebugLn('*** LCLIntfCellRenderer_GetPreferredHeightForWidth *** width=',dbgs(Width));
  // ,' min ',dbgs(minimum_height^),' natural ',dbgs(natural_height^));
  {$ENDIF}
  CellClass := PLCLIntfCellRendererClass(cell^.g_type_instance.g_class);
  if (minimum_height = nil) or (natural_height = nil) then
  begin
    // CellClass^.DefaultGetPreferredHeightForWidth(cell, widget, width, @AMinHeight, @ANaturalHeight);
    // exit;
  end;


  CellClass^.DefaultGetPreferredHeightForWidth(cell, widget, width, minimum_height, natural_height);


  if minimum_height <> nil then
    AMinHeight := minimum_height^
  else
    AMinHeight := 0;
  if natural_height <> nil then
    ANaturalHeight := natural_height^
  else
    ANaturalHeight := 0;

  // writeln('1.minimumheight ',AMinHeight,' naturalheight ',ANaturalHeight,' min ',dbgs(minimum_height <> nil),' nat ',dbgs(natural_height <> nil));

  AWinControl := GetControl(cell, widget);
  if [csDestroying,csLoading]*AWinControl.ComponentState<>[] then exit;

  if AWinControl is TCustomListbox then
    if TCustomListbox(AWinControl).Style < lbOwnerDrawFixed then
      exit;
  if AWinControl is TCustomCombobox then
    if TCustomCombobox(AWinControl).Style < csOwnerDrawVariable then
      exit;

  ItemIndex := GetItemIndex(PLCLIntfCellRenderer(cell), Widget);

  if ItemIndex < 0 then
    ItemIndex := 0;

  MeasureItemStruct.itemID := UINT(ItemIndex);
  MeasureItemStruct.itemWidth := UINT(width);
  MeasureItemStruct.itemHeight := UINT(AMinHeight);
  Msg.Msg := LM_MEASUREITEM;
  Msg.MeasureItemStruct := @MeasureItemStruct;
  TGtk3Widget(AWinControl.Handle).DeliverMessage(Msg);
  if minimum_height <> nil then
    minimum_height^ := gint(MeasureItemStruct.itemHeight);
  if natural_height <> nil then
    natural_height^ := gint(MeasureItemStruct.itemHeight);

  // writeln('Final cell height ',MeasureItemStruct.itemHeight);
end;

procedure LCLIntfCellRenderer_GetSize(cell: PGtkCellRenderer; widget: PGtkWidget;
  cell_area: PGdkRectangle; x_offset: Pgint;
  y_offset: Pgint; width: Pgint; height: Pgint); cdecl;

var
  CellClass: PLCLIntfCellRendererClass;
  AWinControl: TWinControl;
  ItemIndex: Integer;
  Msg: TLMMeasureItem;
  MeasureItemStruct: TMeasureItemStruct;
begin
  // THIS DOES NOT WORK IN Gtk3 ANYMORE.
  {$IFDEF GTK3DEBUGCELLRENDERER}
  DebugLn('******* LCLIntfCellRenderer_GetSize ********');
  {$ENDIF}
  CellClass := PLCLIntfCellRendererClass(cell^.g_type_instance.g_class);
  // CellClass := PLCLIntfCellRendererClass(gtk_object_get_class(cell));
  CellClass^.DefaultGtkGetSize(cell, Widget, cell_area, x_offset, y_offset,
                               width, height);
  //DebugLn(['LCLIntfCellRenderer_GetSize ',GetWidgetDebugReport(Widget)]);
  AWinControl := GetControl(cell, widget);
  if [csDestroying,csLoading]*AWinControl.ComponentState<>[] then exit;

  if AWinControl is TCustomListbox then
    if TCustomListbox(AWinControl).Style < lbOwnerDrawFixed then
      exit;
  if AWinControl is TCustomCombobox then
    if TCustomCombobox(AWinControl).Style < csOwnerDrawVariable then
      exit;

  ItemIndex := GetItemIndex(PLCLIntfCellRenderer(cell), Widget);

  if ItemIndex < 0 then
    ItemIndex := 0;

  MeasureItemStruct.itemID := UINT(ItemIndex);
  MeasureItemStruct.itemWidth := UINT(width^);
  MeasureItemStruct.itemHeight := UINT(height^);
  Msg.Msg := LM_MEASUREITEM;
  Msg.MeasureItemStruct := @MeasureItemStruct;
  TGtk3Widget(AWinControl.Handle).DeliverMessage(Msg);
  width^ := gint(MeasureItemStruct.itemWidth);
  height^ := gint(MeasureItemStruct.itemHeight);
end;

function GtkCellRendererStateToListViewDrawState(CellState: TGtkCellRendererState): TCustomDrawState;
begin
  Result := [];
  if CellState and GTK_CELL_RENDERER_SELECTED > 0 then Result := Result + [cdsSelected];
  if CellState and GTK_CELL_RENDERER_PRELIT > 0 then Result := Result + [cdsHot];
  if CellState and GTK_CELL_RENDERER_INSENSITIVE > 0 then Result := Result + [cdsDisabled, cdsGrayed];
  if CellState and GTK_CELL_RENDERER_FOCUSED > 0 then Result := Result + [cdsFocused];
end;

procedure LCLIntfCellRenderer_Render(cell: PGtkCellRenderer; cr: Pcairo_t;
  Widget: PGtkWidget; background_area: PGdkRectangle; cell_area: PGdkRectangle;
  flags: TGtkCellRendererState); cdecl;
var
  CellClass: PLCLIntfCellRendererClass;
  AWinControl: TWinControl;
  ItemIndex: Integer;
  ColumnIndex: Integer;
  AreaRect: TRect;
  State: TOwnerDrawState;
  Msg: TLMDrawListItem;
  DCWidget: PGtkWidget;
  LVTarget: TCustomDrawTarget;
  LVStage: TCustomDrawStage;
  LVState: TCustomDrawState;
  LVSubItem: Integer;
  TmpDC1,
  TmpDC2: HDC;
  SkipDefaultPaint: Boolean;
begin
  // DebugLn('*** LCLIntfCellRenderer_Render widget=',dbgHex(PtrUInt(Widget)), ' HWND=',dbgs(HwndFromGtkWidget(Widget)));
  {DebugLn(['LCLIntfCellRenderer_Render cell=',dbgs(cell),
    ' ',GetWidgetDebugReport(Widget),' ',
    ' background_area=',dbgGRect(background_area),
    ' cell_area=',dbgGRect(cell_area),
    ' expose_area=',dbgGRect(expose_area)]);}

  ColumnIndex := PLCLIntfCellRenderer(cell)^.ColumnIndex;

  AWinControl := GetControl(cell, widget);

  if (ColumnIndex = -1) and (AWinControl <> nil) and
    (AWinControl.FCompStyle = csListView) then
      ColumnIndex := 0;
  (* debugging
  if Assigned(AWinControl) then
  begin
    DebugLn('*** LCLIntfCellRenderer_Render ',dbgsName(AWinControl),' ColIndex ',dbgs(ColumnIndex));
    if Widget = TGtk3Widget(AWinControl.Handle).Widget then
      DebugLn(' MAIN WIDGET ...')
    else
    if Widget = TGtk3Widget(AWinControl.Handle).GetContainerWidget then
    begin
      DebugLn(' CONTAINER WIDGET ... OK ');
      if wtListBox in TGtk3Widget(AWinControl.Handle).WidgetType then
        ColumnIndex := -1;
      if TGtk3Widget(AWinControl.Handle).Context <> 0 then
        DebugLn(' CONTAINER WIDGET ... HAVE CONTEXT !!!!! ');
    end
    else
    begin
      DebugLn(' FATAL ERROR DONT KNOW WHAT WIDGET IS THIS !');
      if Widget^.parent = TGtk3Widget(AWinControl.Handle).Widget then
        DebugLn(' BUT PARENT IS MAIN WIDGET !')
      else
      if Widget^.parent = TGtk3Widget(AWinControl.Handle).GetContainerWidget then
      begin
        DebugLn(' BUT PARENT IS CONTAINER WIDGET ! CellView=',dbgs(Gtk3IsCellView(Widget)),' w=',dbgHex(PtrUInt(Widget)));
        if Gtk3IsCellView(Widget) then
        begin
          DebugLn(' *** check=', dbgHex(PtrUInt(g_object_get_data(Widget, 'lclwidget'))));
        end;
      end;
    end;
  end;
  *)

  if ColumnIndex > -1 then // listview
  begin
    // DebugLn('Paint 1 ', dbgsName(AWinControl));
    AreaRect := Bounds(background_area^.x, background_area^.y,
                     background_area^.Width, background_area^.Height);


    ItemIndex := GetItemIndex(PLCLIntfCellRenderer(cell), Widget);

    if ItemIndex < 0 then
      ItemIndex := 0;

    if ColumnIndex > 0 then
      LVTarget := dtSubItem
    else
      LVTarget := dtItem;
    if AWinControl.FCompStyle = csListView then
      LVSubItem := ColumnIndex
    else
      LVSubItem := ColumnIndex - 1;
    LVStage := cdPrePaint;
    LVState := GtkCellRendererStateToListViewDrawState(flags);
    DCWidget := Widget;
    TmpDC1 := Gtk3WidgetSet.CreateDCForWidget(DCWidget, nil, cr);
    TmpDC2 := TCustomListViewAccess(AWinControl).Canvas.Handle;
    TCustomListViewAccess(AWinControl).Canvas.Handle := TmpDC1;
    // paint
    SkipDefaultPaint := cdrSkipDefault in TCustomListViewAccess(AWinControl).IntfCustomDraw(LVTarget, LVStage, ItemIndex, LVSubItem, LVState, @AreaRect);

    if SkipDefaultPaint then
    begin
      Gtk3WidgetSet.ReleaseDC(TCustomListViewAccess(AWinControl).Handle,TmpDC1);
      TCustomListViewAccess(AWinControl).Canvas.Handle := TmpDC2;
      Exit;
    end;
  end;

  // draw default
  // CellClass := PLCLIntfCellRendererClass(gtk_object_get_class(cell));
  CellClass := PLCLIntfCellRendererClass(cell^.g_type_instance.g_class);

  // do not call DefaultGtkRender when we are custom drawn listbox.issue #23093
  if (ColumnIndex < 0) and Assigned(AWinControl) then
  begin
    if ([csDestroying,csLoading,csDesigning]*AWinControl.ComponentState<>[]) then
      AWinControl := nil;
    if AWinControl is TCustomListbox then
      if TCustomListbox(AWinControl).Style = lbStandard then
        AWinControl := nil;
    if AWinControl is TCustomCombobox then
      AWinControl := nil;
  end;
  // do default draw only if we are not customdrawn.
  if (ColumnIndex > -1) or ((ColumnIndex < 0) and (AWinControl = nil)) then
  begin
    CellClass^.DefaultGtkRender(cell, cr, Widget, background_area, cell_area,
      flags);
  end;
  
  if ColumnIndex < 0 then  // is a listbox or combobox
  begin
    // send LM_DrawListItem message
    AWinControl := GetControl(cell, widget);
    // DebugLn('Paint 2 ** ', dbgsName(AWinControl));
    if not Assigned(AWinControl) then
    begin
      // DebugLn('Paint 2 ** EXIT ! NO WINCONTROL .... ',dbgHex(PtrUInt(Widget)));
      exit;
    end;
    if [csDestroying,csLoading]*AWinControl.ComponentState<>[] then exit;
  
    // check if the LCL object wants item paint messages
    if AWinControl is TCustomListbox then
      if TCustomListbox(AWinControl).Style = lbStandard then
        exit;
    if AWinControl is TCustomCombobox then
      if TCustomCombobox(AWinControl).Style < csOwnerDrawFixed then
        exit;

    // get itemindex and area
  
    AreaRect := Bounds(background_area^.x, background_area^.y,
                     background_area^.Width, background_area^.Height);

    ItemIndex := GetItemIndex(PLCLIntfCellRenderer(cell), Widget);

    if ItemIndex < 0 then
      ItemIndex := 0;

    // collect state flags
    State:=[odPainted];
    if (flags and GTK_CELL_RENDERER_SELECTED)>0 then
      Include(State, odSelected);
    if not Widget^.is_sensitive then
      Include(State, odInactive);
    if Widget^.has_default then
      Include(State, odDefault);
    if (flags and GTK_CELL_RENDERER_FOCUSED) <> 0 then
      Include(State, odFocused);
    
    if AWinControl is TCustomCombobox then
    begin
      if TCustomComboBox(AWinControl).DroppedDown and ((flags and GTK_CELL_RENDERER_PRELIT)>0) then
        Include(State,odSelected);
    end;
  end else // is a listview
  begin
    LVStage := cdPostPaint;
    // paint
    TCustomListViewAccess(AWinControl).IntfCustomDraw(LVTarget, LVStage, ItemIndex, LVSubItem, LVState, @AreaRect);

    TCustomListViewAccess(AWinControl).Canvas.Handle := TmpDC2;
    Gtk3WidgetSet.ReleaseDC(TCustomListViewAccess(AWinControl).Handle,TmpDC1);
    Exit;
  end;

  // ListBox and ComboBox
  // create message and deliverFillChar(Msg,SizeOf(Msg),0);
  // DebugLn('Paint 4 listbox or combobox  ** ', dbgsName(AWinControl));
  Msg.Msg:=LM_DrawListItem;
  New(Msg.DrawListItemStruct);
  try
    FillChar(Msg.DrawListItemStruct^,SizeOf(TDrawListItemStruct),0);
    with Msg.DrawListItemStruct^ do
    begin
      ItemID := UINT(ItemIndex);
      Area := AreaRect;
      (*
      DebugLn('LCLIntfCellRenderer_Render Widget is: TGtk3Widget.Widget ? ',dbgs(Widget = TGtk3Widget(AWinControl.Handle).Widget),
       ' ContainerWidget ? ',dbgs(Widget = TGtk3Widget(AWinControl.Handle).GetContainerWidget),
        ' CAIRO ?!? ',dbgHex(PtrUInt(cr)),' AreaR ',dbgs(Area),' AreaH ',dbgs(Area.Bottom - Area.Top));
      *)
      DCWidget := Widget;
      if (DCWidget^.parent<>nil) and
        Gtk3IsMenuItem(DCWidget^.parent) then
      begin
        // the Widget is a sub widget of a menu item
        // -> allow the LCL to paint over the whole menu item
        DCWidget := DCWidget^.parent;
        Area := Rect(0,0,DCWidget^.get_allocated_width,DCWidget^.get_allocated_height);
      end;
      DC := GTK3WidgetSet.CreateDCForWidget(DCWidget, nil, cr);
      ItemState:=State;
    end;
    TGtk3Widget(AWinControl.Handle).DeliverMessage(Msg);
    GTK3WidgetSet.ReleaseDC(AWinControl.Handle,Msg.DrawListItemStruct^.DC);
  finally
    Dispose(Msg.DrawListItemStruct);
  end;

  //DebugLn(['LCLIntfCellRenderer_Render END ',DbgSName(LCLObject)]);
end;

procedure LCLIntfCellRenderer_ClassInit(aClass: gpointer; class_data: gpointer); cdecl;
//aClass: PLCLIntfCellRendererClass
var
  LCLClass: PLCLIntfCellRendererClass;
  RendererClass: PGtkCellRendererClass;
begin
  //DebugLn(['LCLIntfCellRenderer_ClassInit ']);
  LCLClass := PLCLIntfCellRendererClass(aClass);
  // check ok
  // PLCLIntfCellRendererClass(g_type_check_class_cast(aClass, LCLIntfCellRenderer_GetType));
  // PLCLIntfCellRendererClass(aClass);
  RendererClass := PGtkCellRendererClass(aClass);
  // check ok
  // RendererClass := PGtkCellRendererClass(g_type_check_class_cast(aClass, gtk_cell_renderer_text_get_type));

  // GTK_CELL_RENDERER_CLASS(aClass);
  LCLClass^.DefaultGetAlignedArea := RendererClass^.get_aligned_area;
  LCLClass^.DefaultGtkGetSize := RendererClass^.get_size;
  LCLClass^.DefaultGtkRender := RendererClass^.render;
  LCLClass^.DefaultGetRequestMode := RendererClass^.get_request_mode;
  LCLClass^.DefaultGetPreferredWidth := RendererClass^.get_preferred_width;
  LCLClass^.DefaultGetPreferredHeight := RendererClass^.get_preferred_height;
  LCLClass^.DefaultGetPreferredHeightForWidth := RendererClass^.get_preferred_height_for_width;
  LCLClass^.DefaultGetPreferredWidthForHeight := RendererClass^.get_preferred_width_for_height;

  RendererClass^.get_request_mode := @LCLIntfCellRenderer_GetRequestMode;
  RendererClass^.get_preferred_width := @LCLIntfCellRenderer_GetPreferredWidth;
  RendererClass^.get_preferred_height := @LCLIntfCellRenderer_GetPreferredHeight;
  RendererClass^.get_preferred_height_for_width := @LCLIntfCellRenderer_GetPreferredHeightForWidth;
  RendererClass^.get_preferred_width_for_height := @LCLIntfCellRenderer_GetPreferredWidthForHeight;
  // this is deprecated so mark in null since GtkCellRenderer checks if get_size is assigned and
  // then it uses old api.
  RendererClass^.get_size := nil; // @LCLIntfCellRenderer_GetSize;
  RendererClass^.get_aligned_area := @LCLIntfCellRenderer_GetAlignedArea;
  RendererClass^.render := @LCLIntfCellRenderer_Render;
end;

procedure LCLIntfCellRenderer_ClassFinalize(g_class: gpointer; class_data: gpointer); cdecl;
begin
  // DebugLn('LCLIntfCellRenderer_ClassFinalize: finalization');
end;

procedure LCLIntfCellRenderer_Init({%H-}Instance:PGTypeInstance;
  {%H-}theClass: Pointer); cdecl;
// Instance: PLCLIntfCellRenderer;
// theClass: PLCLIntfCellRendererClass
begin
  //DebugLn(['LCLIntfCellRenderer_Init ']);
end;

function LCLIntfCellRenderer_GetType: TGType;
const
  CR_NAME = 'LCLIntfCellRenderer';
  crType: TGType = 0;
  crInfo: TGTypeInfo = (
    class_size: SizeOf(TLCLIntfCellRenderer) + 1024;
    base_init: nil; // TGBaseInitFunc;
    base_finalize: nil; // TGBaseFinalizeFunc;
    class_init: @LCLIntfCellRenderer_ClassInit;
    class_finalize: nil; // @LCLIntfCellRenderer_ClassFinalize; // nil; // TGClassFinalizeFunc;
    class_data: nil;
    instance_size: SizeOf(TLCLIntfCellRenderer) + 1024;
    n_preallocs: SizeOf(TLCLIntfCellRenderer) + 1024;
    instance_init: nil; // TGInstanceInitFunc;
    value_table: nil;
  );
begin
  if (crType = 0) then
  begin
    crType := g_type_from_name(CR_NAME);
    if crType = 0 then
      crType := g_type_register_static(gtk_cell_renderer_text_get_type,'LCLIntfCellRenderer',@crInfo, 0);
  end;
  Result := crType;
end;

function LCLIntfCellRenderer_New: PGtkCellRenderer;
begin
  // PGtkCellRenderer(g_type_class_ref(LCLIntfCellRenderer_GetType));
  Result := g_object_new(LCLIntfCellRenderer_GetType, nil,[]);
end;

procedure LCLIntfCellRenderer_CellDataFunc(cell_layout:PGtkCellLayout;
  cell: PGtkCellRenderer; tree_model: PGtkTreeModel; iter: PGtkTreeIter;
  data: gpointer); cdecl;
var
  LCLCellRenderer: PLCLIntfCellRenderer absolute cell;
  APath: PGtkTreePath;
  S: String;
  // Str: PgChar;
  ListColumn: TListColumn;
  ListItem: TListItem;
  Value: TGValue;
  AMinH: gint;
  ANatH: gint;
begin
  if not Gtk3IsObject(cell) then
    exit;

  ListItem := nil;
  APath := gtk_tree_model_get_path(tree_model,iter);
  LCLCellRenderer^.Index := gtk_tree_path_get_indices(APath)^;
  LCLCellRenderer^.ColumnIndex := -1;
  gtk_tree_path_free(APath);

  // DebugLn('LCLCellRenderer^.Index=',dbgs(LCLCellRenderer^.Index));

  // WidgetInfo := PWidgetInfo(data);
  // DebugLn(['LCLIntfCellRenderer_CellDataFunc stamp=',iter^.stamp,' tree_model=',dbgs(tree_model),' cell=',dbgs(cell),' WidgetInfo=',WidgetInfo <> nil,' Time=',TimeToStr(Now)]);

  if (wtComboBox in TGtk3Widget(Data).WidgetType) and
    (TCustomComboBox(TGtk3Widget(Data).LCLObject).Style = csDropDownList) and
    not (TCustomComboBox(TGtk3Widget(Data).LCLObject).DroppedDown) then
  begin
    Value.g_type := G_TYPE_UINT;
    Value.data[0].v_uint := 0;
    g_object_get_property(PgObject(cell),'ypad',@Value);
    Value.data[0].v_int := 0;
    g_object_set_property(PGObject(cell), 'ypad', @Value);
  end else
  if wtListView in TGtk3Widget(Data).WidgetType then
  begin
    // DebugLn(['LCLIntfCellRenderer_CellDataFunc stamp=',iter^.stamp,' tree_model=',dbgs(tree_model),' cell=',dbgs(cell),' WidgetInfo=',WidgetInfo <> nil,' Time=',TimeToStr(Now)]);
    Value.g_type := G_TYPE_STRING;
    gtk_tree_model_get(tree_model, iter, [0, @ListItem, -1]);
    if (ListItem = nil) and TCustomListView(TGtk3Widget(Data).LCLObject).OwnerData then
      ListItem := TCustomListView(TGtk3Widget(Data).LCLObject).Items[LCLCellRenderer^.Index];

    if ListItem = nil then
      Exit;

    ListColumn := TListColumn(g_object_get_data(PGObject(cell_layout), 'TListColumn'));

    if ListColumn = nil then
      LCLCellRenderer^.ColumnIndex := -1
    else
      LCLCellRenderer^.ColumnIndex := ListColumn.Index;

    S := '';
    if LCLCellRenderer^.ColumnIndex <= 0 then
      S := ListItem.Caption
    else
      if ListColumn.Index-1 <= ListItem.SubItems.Count-1 then
        S := ListItem.SubItems.Strings[LCLCellRenderer^.ColumnIndex-1];

    Value.data[0].v_pointer := PgChar(S);
    cell^.set_property('text', @Value);
  end else
  if (wtListBox in TGtk3Widget(Data).WidgetType) then
  begin
    if TGtk3ListBox(Data).ListBoxStyle < lbOwnerDrawFixed then
    begin
      Value.g_type := G_TYPE_STRING;
      Value.data[0].v_pointer := nil;
      cell^.get_property('text', @Value);
      // DebugLn('PropertyType=',dbgs(Value.g_type),' IsString=',dbgs(Value.g_type = G_TYPE_STRING),' getString=',Value.get_string);

      S := TCustomListBox(TGtk3Widget(Data).LCLObject).Items.Strings[LCLCellRenderer^.Index];
      // DebugLn('LCLCellRenderer^.Index=',dbgs(LCLCellRenderer^.Index),' text=',Str);
      Value.data[0].v_pointer := PgChar(S);
      // Value.set_string(Str);
      // set text only if we are not ownerdrawn !
      cell^.set_property('text', @Value);
      // DebugLn('IsFixedCellSize ',dbgs(PGtkTreeView(TGtk3Widget(Data).GetContainerWidget)^.get_fixed_height_mode));
    end else
    begin
      //
    end;
  end;

  //DebugLn(['LCLIntfCellRenderer_CellDataFunc ItemIndex=',LCLCellRenderer^.Index]);
end;

procedure LCLIntfRenderer_ColumnCellDataFunc(tree_column: PGtkTreeViewColumn;
  cell: PGtkCellRenderer; tree_model: PGtkTreeModel; iter: PGtkTreeIter;
  data: gpointer); cdecl;
begin
  {$IFDEF GTK3DEBUGCELLRENDERER}
  DebugLn('******* LCLIntfRenderer_ColumnCellDataFunc ********');
  {$ENDIF}
  LCLIntfCellRenderer_CellDataFunc(PGtkCellLayout(tree_column), cell, tree_model, iter, data);
end;

procedure LCLIntfRenderer_GtkCellLayoutDataFunc(cell_layout: PGtkCellLayout;
  cell: PGtkCellRenderer; tree_model: PGtkTreeModel; iter: PGtkTreeIter;
  data: gpointer); cdecl;
begin
  DebugLn('******* LCLIntfRenderer_GtkCellLayoutDataFunc ********');
end;

end.
