{%MainUnit gtkwscomctrls.pp}
{ $Id: gtkwscustomlistview.inc 57164 2018-01-27 18:12:35Z ondrej $

 *****************************************************************************
  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.
 *****************************************************************************
}


{ TGtkWSCustomListView }

type
  TLVHack = class(TCustomListView)
  end;
  
  PCustomListViewData = ^TCustomListViewData;
  TCustomListViewData = record
    ScrollingData: TBaseScrollingWinControlData;
    ViewStyle: TViewStyle;
  end;
  
////////////////////////////////////////////////////////////////////////////////
// Event code
////////////////////////////////////////////////////////////////////////////////


  
//----------------------
//HDN_ENDTRACK
//HDN_TRACK
function GtkWSCustomListView_AbortColumnResize(AList: PGTKCList; AInfo: PWidgetInfo): GBoolean; cdecl;
begin
  //TODO: implement
  Result := False;
end;

//----------------------
//HDN_ENDTRACK
//HDN_TRACK
//HDN_ITEMCHANGED
//HDN_ITEMCHANGING
function GtkWSCustomListView_ResizeColumn(AList: PGTKCList; AColumn, AWidth: Integer; AInfo: PWidgetInfo): GBoolean; cdecl;
begin
  //TODO: implement
  Result := False;
end;

//----------------------
//HDN_ITEMCLICK
//LVN_COLUMNCLICK
function GtkWSCustomListView_ClickColumn(AList: PGTKCList; AColumn: Integer; AInfo: PWidgetInfo): GBoolean; cdecl;
var
  msg: TLMNotify;
  NM: TNMListView;
  ALV: TListView;
begin
  // this can happen when no columns are added which crashes the program
  ALV := TListView(AInfo^.LCLObject);
  if AColumn > ALV.Columns.Count-1 then
    Exit;
    
  msg.Msg := CN_NOTIFY;

  FillChar(NM, SizeOf(NM), 0);
  NM.hdr.hwndfrom := PtrUInt(AList);
  NM.hdr.code := LVN_COLUMNCLICK;
  NM.iItem := -1;
  NM.iSubItem := AColumn;
  msg.NMHdr := @NM.hdr;
  Result := DeliverMessage(AInfo^.LCLObject, msg) = 0;
end;

//----------------------
//LVN_DELETEITEM
//LVN_INSERTITEM
function GtkWSCustomListView_RowMove(AList: PGTKCList; AnOldIdx, ANewIdx: Integer; AInfo: PWidgetInfo): GBoolean; cdecl;
var
  msg: TLMNotify;
  NM: TNMListView;
  r: Boolean;
begin
  // Simulate move by remove and insert
  msg.Msg := CN_NOTIFY;

  FillChar(NM, SizeOf(NM), 0);
  NM.hdr.hwndfrom := PtrUInt(AList);
  NM.hdr.code := LVN_DELETEITEM;
  NM.iItem := AnOldIdx;
  msg.NMHdr := @NM.hdr;
  r := DeliverMessage(AInfo^.LCLObject, msg) = 0;

  NM.hdr.code := LVN_INSERTITEM;
  NM.iItem := ANewIdx;
  Result := (DeliverMessage(AInfo^.LCLObject, msg) = 0) and r;
end;

//----------------------
//LVN_ITEMCHANGED
//LVN_ITEMCHANGING
function GtkWSCustomListView_SelectRow(AList: PGTKCList; ARow, AColumn: Integer; AEvent: PGDKEventButton; AInfo: PWidgetInfo): GBoolean; cdecl;
var
  msg: TLMNotify;
  NM: TNMListView;
begin
  msg.Msg := CN_NOTIFY;

  FillChar(NM, SizeOf(NM), 0);
  NM.hdr.hwndfrom := PtrUInt(AList);
  NM.hdr.code := LVN_ITEMCHANGED;
  NM.iItem := ARow;
  NM.iSubItem := AColumn;
  NM.uNewState := LVIS_SELECTED;
  NM.uChanged := LVIF_STATE;
  msg.NMHdr := @NM.hdr;
  Result := DeliverMessage(AInfo^.LCLObject, msg) = 0;
end;

function GtkWSCustomListView_UnSelectRow(AList: PGTKCList; ARow, AColumn: Integer; AEvent: PGDKEventButton; AInfo: PWidgetInfo): GBoolean; cdecl;
var
  msg: TLMNotify;
  NM: TNMListView;
begin
  msg.Msg := CN_NOTIFY;

  FillChar(NM, SizeOf(NM), 0);
  NM.hdr.hwndfrom := PtrUInt(AList);
  NM.hdr.code := LVN_ITEMCHANGED;
  NM.iItem := ARow;
  NM.iSubItem := AColumn;
  NM.uOldState := LVIS_SELECTED;
  NM.uChanged := LVIF_STATE;
  msg.NMHdr := @NM.hdr;
  Result := DeliverMessage(AInfo^.LCLObject, msg) = 0;
end;

function GtkWSCustomListView_ToggleFocusRow(AList: PGTKCList; AInfo: PWidgetInfo): GBoolean; cdecl;
var
  msg: TLMNotify;
  NM: TNMListView;
begin
  // the defocus of the oldrow isn't send

  msg.Msg := CN_NOTIFY;

  FillChar(NM, SizeOf(NM), 0);
  NM.hdr.hwndfrom := PtrUInt(AList);
  NM.hdr.code := LVN_ITEMCHANGED;
  NM.iItem := AList^.focus_row;
  NM.iSubItem := 0;
  NM.uNewState := LVIS_FOCUSED;
  NM.uChanged := LVIF_STATE;
  msg.NMHdr := @NM.hdr;
  Result := DeliverMessage(AInfo^.LCLObject, msg) = 0;
end;

function GtkWSCustomListView_SelectAll(AList: PGTKCList; AInfo: PWidgetInfo): GBoolean; cdecl;
var
  msg: TLMNotify;
  NM: TNMListView;
  ListView: TListView;
  n: Integer;
begin
  msg.Msg := CN_NOTIFY;

  ListView := AInfo^.LCLObject as TListView;

  FillChar(NM, SizeOf(NM), 0);
  NM.hdr.hwndfrom := PtrUInt(AList);
  NM.hdr.code := LVN_ITEMCHANGED;
  for n := 0 to Listview.Items.Count - 1 do
  begin
    if ListView.Items[n].Selected
    then Continue;
    NM.iItem := n;
    NM.iSubItem := -1;
    NM.uNewState := LVIS_SELECTED;
    NM.uChanged := LVIF_STATE;
    msg.NMHdr := @NM.hdr;
    Result := DeliverMessage(AInfo^.LCLObject, msg) = 0;
  end;
end;

function GtkWSCustomListView_UnSelectAll(AList: PGTKCList; AInfo: PWidgetInfo): GBoolean; cdecl;
var
  msg: TLMNotify;
  NM: TNMListView;
  ListView: TListView;
  n: Integer;
begin
  msg.Msg := CN_NOTIFY;

  ListView := AInfo^.LCLObject as TListView;

  FillChar(NM, SizeOf(NM), 0);
  NM.hdr.hwndfrom := PtrUInt(AList);
  NM.hdr.code := LVN_ITEMCHANGED;
  for n := 0 to Listview.Items.Count - 1 do
  begin
    if not ListView.Items[n].Selected
    then Continue;
    NM.iItem := n;
    NM.iSubItem := -1;
    NM.uOldState := LVIS_SELECTED;
    NM.uChanged := LVIF_STATE;
    msg.NMHdr := @NM.hdr;
    Result := DeliverMessage(AInfo^.LCLObject, msg) = 0;
  end;
end;

function GtkWSCustomListView_EndSelection(AList: PGTKCList; AInfo: PWidgetInfo): GBoolean; cdecl;
begin
  Result:=true;
end;

////////////////////////////////////////////////////////////////////////////////
// Column code
////////////////////////////////////////////////////////////////////////////////

class procedure TGtkWSCustomListView.ColumnDelete(const ALV: TCustomListView; const AIndex: Integer);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'ColumnDelete') then Exit;

  // allow only column modifications when in report mode
  if TLVHack(ALV).ViewStyle <> vsReport then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  if CListWidget^.columns = TLVHack(ALV).Columns.Count then Exit; // possible delayed update
  if AIndex >= CListWidget^.columns then Exit; // ???
  
  RecreateWnd(ALV);
end;

class function TGtkWSCustomListView.ColumnGetWidth(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn): Integer;
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
  CListColumn: PGtkCListColumn;
begin
  Result := -1;
  if not WSCheckHandleAllocated(ALV, 'ColumnGetSize') then Exit;

  // allow only column modifications when in report mode
  if TLVHack(ALV).ViewStyle <> vsReport then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);

  // there is no get width function, so we need some internal hacking
  if AIndex >= CListWidget^.columns then Exit;
  CListColumn := CListWidget^.Column;
  Inc(CListColumn, AIndex);
  Result := CListColumn^.width;
end;

class procedure TGtkWSCustomListView.ColumnInsert(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'ColumnInsert') then Exit;

  // allow only column modifications when in report mode
  if TLVHack(ALV).ViewStyle <> vsReport then Exit;
  
  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  if CListWidget^.columns = TLVHack(ALV).Columns.Count then Exit; // possible delayed update

  RecreateWnd(ALV);
end;

class procedure TGtkWSCustomListView.ColumnMove(const ALV: TCustomListView; const AOldIndex, ANewIndex: Integer; const AColumn: TListColumn);
  procedure CopyColumn(const AList: PGtkCList; const AIndex: Integer; const ASrc: PGtkCListColumn);
  begin                                      
    gtk_clist_set_column_title(AList, AIndex, ASrc^.title);
    gtk_clist_set_column_min_width(AList, AIndex, ASrc^.min_width);
    gtk_clist_set_column_max_width(AList, AIndex, ASrc^.max_width);
    gtk_clist_set_column_width(AList, AIndex, ASrc^.width);
    gtk_clist_set_column_justification(AList, AIndex, ASrc^.justification);
    gtk_clist_set_column_visibility(AList, AIndex, (ASrc^.flag0 and bm_TGtkCListColumn_visible) <> 0);
    gtk_clist_set_column_resizeable(AList, AIndex, (ASrc^.flag0 and bm_TGtkCListColumn_resizeable) <> 0);
    gtk_clist_set_column_auto_resize(AList, AIndex, (ASrc^.flag0 and bm_TGtkCListColumn_auto_resize) <> 0);
    if (ASrc^.flag0 and bm_TGtkCListColumn_button_passive) <> 0
    then gtk_clist_column_title_passive(AList, AIndex)
    else gtk_clist_column_title_active(AList, AIndex);
  end;
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
  CListColumn: PGtkCListColumn;
  OldCListColumn: TGtkCListColumn;
  Count: Integer;      
  
begin
  if not WSCheckHandleAllocated(ALV, 'ColumnMove') then Exit;

  // allow only column modifications when in report mode
  if TLVHack(ALV).ViewStyle <> vsReport then Exit;

  if AOldIndex = ANewIndex then Exit;
  if AOldIndex < 0 then Exit;
  if ANewIndex < 0 then Exit;
  
  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  
  if AOldIndex >= CListWidget^.columns then Exit;
  if ANewIndex >= CListWidget^.columns then Exit;
  
  Count := AOldIndex - ANewIndex;
  
  // Fetch old column values
  CListColumn := CListWidget^.Column;
  Inc(CListColumn, AOldIndex);
  OldCListColumn := CListColumn^; 
  // Create copy of the title
  OldCListColumn.title := StrNew(OldCListColumn.title);

  while Count <> 0 do
  begin
    // move to next source
    if Count < 0
    then Inc(CListColumn)
    else Dec(CListColumn);

    CopyColumn(CListWidget, ANewIndex + Count, CListColumn);

    if Count < 0
    then Inc(Count)
    else Dec(Count);
  end;
  // finally copy original data to new column
  CopyColumn(CListWidget, ANewIndex, @OldCListColumn);
  // dispose copy of the title
  StrDispose(OldCListColumn.title);
end;

class procedure TGtkWSCustomListView.ColumnSetAlignment(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AAlignment: TAlignment);
const
  JUSTIFICATION: array[TAlignment] of TGtkJustification = (
    GTK_JUSTIFY_LEFT,
    GTK_JUSTIFY_RIGHT,
    GTK_JUSTIFY_CENTER
  );
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'ColumnSetAlignment') then Exit;

  // allow only column modifications when in report mode
  if TLVHack(ALV).ViewStyle <> vsReport then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);

  gtk_clist_set_column_justification(CListWidget, AIndex, JUSTIFICATION[AAlignment]);
end;

class procedure TGtkWSCustomListView.ColumnSetAutoSize(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AAutoSize: Boolean);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'ColumnSetAutoSize') then Exit;

  // allow only column modifications when in report mode
  if TLVHack(ALV).ViewStyle <> vsReport then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);

  gtk_clist_set_column_auto_resize(CListWidget, AIndex, AAutoSize);
end;

class procedure TGtkWSCustomListView.ColumnSetCaption(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const ACaption: String);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'ColumnSetCaption') then Exit;

  // allow only column modifications when in report mode
  if TLVHack(ALV).ViewStyle <> vsReport then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);

  gtk_clist_set_column_title(CListWidget, AIndex, PChar(ACaption));
end;

class procedure TGtkWSCustomListView.ColumnSetImage(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AImageIndex: Integer);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'ColumnSetImage') then Exit;

  // allow only column modifications when in report mode
  if TLVHack(ALV).ViewStyle <> vsReport then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);

  //TODO
  if CListWidget=nil then exit;
end;

class procedure TGtkWSCustomListView.ColumnSetMaxWidth(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AMaxWidth: Integer);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'ColumnSetMaxWidth') then Exit;

  // allow only column modifications when in report mode
  if TLVHack(ALV).ViewStyle <> vsReport then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);


  // TODO: ? -1 -2
  //LVSCW_AUTOSIZE              = -1;
  //LVSCW_AUTOSIZE_USEHEADER    = -2;

  if AMaxWidth <= 0 // unlimited
  then gtk_clist_set_column_max_width(CListWidget, AIndex, -1)
  else gtk_clist_set_column_max_width(CListWidget, AIndex, AMaxWidth);
end;

class procedure TGtkWSCustomListView.ColumnSetMinWidth(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AMinWidth: integer);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'ColumnSetMinWidth') then Exit;

  // allow only column modifications when in report mode
  if TLVHack(ALV).ViewStyle <> vsReport then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);

  gtk_clist_set_column_min_width(CListWidget, AIndex, AMinWidth);
end;

class procedure TGtkWSCustomListView.ColumnSetWidth(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AWidth: Integer);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'ColumnSetWidth') then Exit;

  // allow only column modifications when in report mode
  if TLVHack(ALV).ViewStyle <> vsReport then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);

  gtk_clist_set_column_width(CListWidget, AIndex, AWidth);
end;

class procedure TGtkWSCustomListView.ColumnSetVisible(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AVisible: Boolean);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'ColumnSetVisible') then Exit;

  // allow only column modifications when in report mode
  if TLVHack(ALV).ViewStyle <> vsReport then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);

  gtk_clist_set_column_visibility(CListWidget, AIndex, AVisible);
end;

////////////////////////////////////////////////////////////////////////////////
// Item code
////////////////////////////////////////////////////////////////////////////////

class procedure TGtkWSCustomListView.ItemChangeInternal(const ACListWidget: PGtkCList; const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem);
var
  ImageBitmap: TBitmap;
  GDIObject: PGDIObject;
  Pixmap: PGdkPixmap;
  Mask: PGdkBitmap;
  n, Count: integer;
begin
  if  (TLVHack(ALV).SmallImages <> nil)
  and (AItem.ImageIndex >= 0)
  and (AItem.ImageIndex < TLVHack(ALV).SmallImages.Count)
  then begin
    // set image & caption
    ImageBitmap := TBitmap.Create;
    Mask := nil;
    try
      TLVHack(ALV).SmallImages.GetBitmap(AItem.ImageIndex, ImageBitmap);
      GDIObject := PGDIObject(ImageBitmap.Handle);
      case GDIObject^.GDIBitmapType of
        gbBitmap:
          begin
            Pixmap := GDIObject^.GDIBitmapObject;
            Mask := nil;
          end;
        gbPixmap:
          begin
            Pixmap := GDIObject^.GDIPixmapObject.Image;
            Mask := CreateGdkMaskBitmap(ImageBitmap.Handle, ImageBitmap.MaskHandle);
          end;
        gbPixbuf:
          begin
            Pixmap := nil;
            Mask := nil;
            gdk_pixbuf_render_pixmap_and_mask(GDIObject^.GDIPixbufObject, Pixmap, Mask, $80);
          end;
      end;
      gtk_clist_set_pixtext(ACListWidget, AIndex, 0, PChar(AItem.Caption), 3, Pixmap, Mask);
    finally
      if Mask <> nil then
        gdk_bitmap_unref(Mask);
      if Pixmap <> GDIObject^.GDIPixmapObject.Image then
        gdk_pixmap_unref(Pixmap);
      ImageBitmap.Free;
    end;
  end
  else begin
    // set caption alone
    gtk_clist_set_text(ACListWidget, AIndex, 0, PChar(AItem.Caption));
  end;

  // set the other column texts
  Count := AItem.SubItems.Count + 1;
  if Count > ACListWidget^.Columns
  then Count := ACListWidget^.Columns;
  // set the existing subitems
  for n := 1 to Count - 1 do
    gtk_clist_set_text(ACListWidget, AIndex, n, PChar(AItem.SubItems[n - 1]));
  // fill remaining
  for n := Count to ACListWidget^.Columns - 1 do
    gtk_clist_set_text(ACListWidget, AIndex, n, #0);
end;

class procedure TGtkWSCustomListView.ItemDelete(const ALV: TCustomListView;
  const AIndex: Integer);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'ItemDelete')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);

  gtk_clist_remove(CListWidget, AIndex);
end;

class procedure TGtkWSCustomListView.ItemExchange(const ALV: TCustomListView;
  AItem: TListItem; const AIndex1, AIndex2: Integer);
begin
  if not WSCheckHandleAllocated(ALV, 'ItemExchange') then
    exit;
  ItemMove(ALV, AItem, AIndex1, AIndex2);
  if AIndex1 > AIndex2 then
    ItemMove(ALV, AItem, AIndex2 + 1, AIndex1)
  else
    ItemMove(ALV, AItem, AIndex2 - 1, AIndex1);
end;

class procedure TGtkWSCustomListView.ItemMove(const ALV: TCustomListView;
  AItem: TListItem; const AFromIndex, AToIndex: Integer);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'ItemMove') then
    exit;
  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  gtk_clist_row_move(CListWidget, AFromIndex, AToIndex);
end;

class function TGtkWSCustomListView.ItemGetState(const ALV: TCustomListView;
  const AIndex: Integer; const AItem: TListItem; const AState: TListItemState;
  out AIsSet: Boolean): Boolean;
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  Result := False;
  
  if not WSCheckHandleAllocated(ALV, 'ItemGetState')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  if (AIndex < 0) or (AIndex >= CListWidget^.rows) 
  then begin
    DebugLN('[TGtkWSCustomListView.ItemGetState] Invalid row index: %d', [Aindex]);
    Exit;
  end;   
  
  case AState of
    lisCut, 
    lisDropTarget: begin
      //TODO: do something with the rowcolor ?
    end;

    lisFocused: begin
      AIsSet := CListWidget^.focus_row = AIndex;
      Result := True;
    end;

    lisSelected: begin 
      AIsSet := (CListWidget^.selection <> nil)
            and (g_list_find(CListWidget^.selection, Pointer(PtrInt(Aindex))) <> nil);
      Result := True;
    end;
  end;
  
end;
  
class procedure TGtkWSCustomListView.ItemInsert(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
  Titles: PPGChar;
  idx, Count: Integer;
begin
  if not WSCheckHandleAllocated(ALV, 'ItemInsert')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);

  Count := CListWidget^.columns;

  if Count = 0
  then begin
    DebugLn('WARNING: TGtkWSCustomListView.ItemInsert  CListWidget^.columns = 0');
    Exit;
  end;

  GetMem(Titles, SizeOf(PGChar) * Count);
  FillChar(Titles^, SizeOf(PGChar) * Count, 0);
  Titles[0] := #0;

  idx := AIndex;
  if idx = -1
  then idx := gtk_clist_append(CListWidget, Titles)
  else gtk_clist_insert(CListWidget, idx, Titles);
  FreeMem(Titles);

  ItemChangeInternal(CListWidget, ALV, idx, AItem);
end;

class procedure TGtkWSCustomListView.ItemSetImage(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const ASubIndex, AImageIndex: Integer);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
  ImageBitmap: TBitmap;
  Pixmap: PGdkPixmap;
  Mask: PGdkBitmap;
  Spacing: guint8;
  Text: PChar;
  Dummy1, Dummy2: PGdkBitmap;
  CellType: TGtkCellType;
begin
  if not WSCheckHandleAllocated(ALV, 'ItemSetImage')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);

  Pixmap := nil;
  Mask := nil;

  if  (TLVHack(ALV).SmallImages <> nil)
  and (AImageIndex >= 0)
  and (AImageIndex < TLVHack(ALV).SmallImages.Count)
  then begin
    // set image & caption
    ImageBitmap := TBitmap.Create;
    try
      TLVHack(ALV).SmallImages.GetBitmap(AImageIndex, ImageBitmap);
      case PGDIObject(ImageBitmap.Handle)^.GDIBitmapType of
        gbBitmap:
          begin
            Pixmap := PGDIObject(ImageBitmap.Handle)^.GDIBitmapObject;
            gdk_pixmap_ref(Pixmap);
            Mask := nil;
          end;
        gbPixmap:
          begin
            Pixmap := PGDIObject(ImageBitmap.Handle)^.GDIPixmapObject.Image;
            Mask := CreateGdkMaskBitmap(ImageBitmap.Handle, ImageBitmap.MaskHandle);
            gdk_pixmap_ref(Pixmap);
          end;
        gbPixbuf:
          begin
            Pixmap := nil;
            Mask := nil;
            gdk_pixbuf_render_pixmap_and_mask(PGDIObject(ImageBitmap.Handle)^.GDIPixbufObject, pixmap, mask, $80);
          end;
      end;
    finally
      ImageBitmap.Free;
    end;
  end;

  CellType := gtk_clist_get_cell_type(CListWidget, AIndex, ASubIndex);
  // Sigh. 
  // gtk returns -1 for an invalid cell (which is not part of the enum)
  // so to handle it, we need a case based on integer
  case Ord(CellType) of
    Ord(GTK_CELL_TEXT),
    Ord(GTK_CELL_PIXTEXT),
    Ord(GTK_CELL_EMPTY),
    Ord(GTK_CELL_PIXMAP): begin
      if pixmap <> nil
      then begin
        case CellType of
          GTK_CELL_TEXT: begin
            // convert the cell
            Text := nil;
            gtk_clist_get_text(CListWidget, AIndex, ASubIndex, @Text);
            gtk_clist_set_pixtext(CListWidget, AIndex, ASubIndex, Text, DEFAULT_IMAGE_SPACING, Pixmap, Mask);
          end;
          GTK_CELL_PIXTEXT: begin
            if gtk_clist_get_pixtext(CListWidget, AIndex, ASubIndex, @Text, @Spacing, @Dummy2, @Dummy1) <> 0
            then gtk_clist_set_pixtext(CListWidget, AIndex, ASubIndex, Text, Spacing, Pixmap, Mask)
            else gtk_clist_set_pixmap(CListWidget, AIndex, ASubIndex, Pixmap, Mask);
          end;
          GTK_CELL_EMPTY,
          GTK_CELL_PIXMAP: begin
            gtk_clist_set_pixtext(CListWidget, AIndex, ASubIndex, '', DEFAULT_IMAGE_SPACING, Pixmap, Mask);
          end;
        end;
      end
      else begin
        case CellType of
          GTK_CELL_EMPTY,
          GTK_CELL_TEXT:; // nothing to do
          GTK_CELL_PIXTEXT: begin
            Text := nil;
            if gtk_clist_get_pixtext(CListWidget, AIndex, ASubIndex, @Text, @Spacing, @Dummy2, @Dummy1) <> 0
            then gtk_clist_set_text(CListWidget, AIndex, ASubIndex, Text)
            else gtk_clist_set_text(CListWidget, AIndex, ASubIndex, '');
          end;
          GTK_CELL_PIXMAP: begin
            gtk_clist_set_text(CListWidget, AIndex, ASubIndex, '');
          end;
        end;
      end;
    end;
    Ord(GTK_CELL_WIDGET): DebugLN('[TGtkWSCustomListView.ItemSetImage] Setting text of widget cell');
    -1: DebugLN('[TGtkWSCustomListView.ItemSetText] Cell (%d,%d) not created', [AIndex, ASubIndex]);
  else
    DebugLN('[TGtkWSCustomListView.ItemSetImage] Unknown celltype %d', [Ord(CellType)]);
  end;
  if Pixmap <> nil then
    gdk_pixmap_unref(Pixmap);
  if Mask <> nil then
    gdk_bitmap_unref(Mask);
end;

class procedure TGtkWSCustomListView.ItemSetState(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const AState: TListItemState; const AIsSet: Boolean);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'ItemSetState')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  if (AIndex < 0) or (AIndex >= CListWidget^.rows) 
  then begin
    DebugLN('[TGtkWSCustomListView.ItemSetState] Invalid row index: %d', [Aindex]);
    Exit;
  end;   
  
  case AState of
    lisCut, 
    lisDropTarget: begin
      //TODO: do something with the rowcolor ?
    end;

    lisFocused: begin
      if AIsSet = (CListWidget^.focus_row = AIndex) then Exit;
      // reset old focus
      if (CListWidget^.focus_row <> -1)
      and (gtk_widget_has_focus(PGtkWidget(CListWidget)))
      then gtk_widget_draw_focus(PGtkWidget(CListWidget));
      
      if AIsSet
      then begin
        CListWidget^.focus_row := AIndex;
        if gtk_widget_has_focus(PGtkWidget(CListWidget))
        then gtk_widget_draw_focus(PGtkWidget(CListWidget));
      end
      else CListWidget^.focus_row := -1; 
    end;

    lisSelected: begin
      if AIsSet
      then begin
        if (CListWidget^.selection_mode = GTK_SELECTION_SINGLE)
        or (CListWidget^.selection_mode = GTK_SELECTION_BROWSE)
        then begin
          // check if the row is are already selected
          // since we are in singleselect, the first item is checked
          if (CListWidget^.selection <> nil)
          and (PtrInt(PtrUInt(CListWidget^.selection^.Data)) = AIndex)
          then Exit;
          gtk_clist_unselect_all(CListWidget);
        end;
        gtk_clist_select_row(CListWidget, AIndex, 0);
      end
      else gtk_clist_unselect_row(CListWidget, AIndex, 0);
    end;
  end;
end;

class procedure TGtkWSCustomListView.ItemSetText(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const ASubIndex: Integer; const AText: String);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
  Pixmap: PGdkPixmap;
  Mask: PGdkBitmap;
  Spacing: guint8;
  Dummy: pgchar;
  CellType: TGtkCellType;
begin
  if not WSCheckHandleAllocated(ALV, 'ItemSetText')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);

  CellType := gtk_clist_get_cell_type(CListWidget, AIndex, ASubIndex);
  // Sigh. 
  // gtk returns -1 for an invalid cell (which is not part of the enum)
  // so to handle it, we need a case based on integer
  case Ord(CellType) of
    Ord(GTK_CELL_EMPTY),
    Ord(GTK_CELL_TEXT): begin
      // simply set the text
      gtk_clist_set_text(CListWidget, AIndex, ASubIndex, PChar(AText));
    end;
    Ord(GTK_CELL_PIXTEXT): begin
      if gtk_clist_get_pixtext(CListWidget, AIndex, ASubIndex, @Dummy, @Spacing, @Pixmap, @Mask) <> 0
      then gtk_clist_set_pixtext(CListWidget, AIndex, ASubIndex, PChar(AText), Spacing, Pixmap, Mask)
      else gtk_clist_set_text(CListWidget, AIndex, ASubIndex, PChar(AText));
    end;
    Ord(GTK_CELL_PIXMAP): begin
      if gtk_clist_get_pixmap(CListWidget, AIndex, ASubIndex, @Pixmap, @Mask) <> 0
      then gtk_clist_set_pixtext(CListWidget, AIndex, ASubIndex, PChar(AText), DEFAULT_IMAGE_SPACING, Pixmap, Mask)
      else gtk_clist_set_text(CListWidget, AIndex, ASubIndex, PChar(AText));
    end;
    Ord(GTK_CELL_WIDGET): DebugLN('[TGtkWSCustomListView.ItemSetText] Setting text of widget cell');
    -1: DebugLN('[TGtkWSCustomListView.ItemSetText] Cell (%d,%d) not created', [AIndex, ASubIndex]);
  else
    DebugLN('[TGtkWSCustomListView.ItemSetText] Unknown celltype %d', [Ord(CellType)]);
  end;
end;

class procedure TGtkWSCustomListView.ItemShow(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const PartialOK: Boolean);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
  RowTopY: Integer;
begin
  if not WSCheckHandleAllocated(ALV, 'ItemShow')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  
  RowTopY := (CListWidget^.row_height * AIndex) + ((AIndex +1) *
                                     {CELL} 1 {SPACING} + CListWidget^.voffset);
                                     
                                                 // 0=NotVisible
                                                 // 1=PartiallyVisible
                                                 // 2=Fully Visible
                                                 //   |
  if gtk_clist_row_is_visible(CListWidget, AIndex) < (2 - Ord(PartialOK)) then begin
    if (RowTopY + CListWidget^.row_height > CListWidget^.clist_window_height) then begin
      gtk_clist_moveto (CListWidget, AIndex, -1, 1, 0);
        //                              |     |  |  |
        //                        The Row     |  |  |
        //                           The Column  |  |
        //                               Row Align  |
    end //                               Column Align
    else if (RowTopY < 0) then begin
      gtk_clist_moveto (CListWidget, AIndex, -1, 0, 0);
    end;//                                       |
  end;  //                                       |
        //                                       |
        //                      0 = your row will be at the top.
        //                      1 = it will be at the bottom.
end;

////////////////////////////////////////////////////////////////////////////////
// LV code
////////////////////////////////////////////////////////////////////////////////

class function TGtkWSCustomListView.CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): HWND;
var
  ListView: TLVHack; //TCustomListView
  WidgetInfo: PWidgetInfo;
  ScrollingData: PBaseScrollingWinControlData;
  ListViewData: PCustomListViewData;

  ScrollWidget: PGtkScrolledWindow;
  CListWidget: PGtkCList;
begin
  ListView := TLVHack(AWinControl as TCustomListView);

  Result := TGtkWSBaseScrollingWinControl.CreateHandle(AWinControl, AParams);
  if Result = 0 then Exit;
  ScrollWidget := PGtkScrolledWindow(Result);

  if ListView.ViewStyle = vsReport
  then begin
    // precreate colums since they cannot be added or removed
    CListWidget := PGtkCList(gtk_clist_new(Max(1, ListView.Columns.Count)));
    gtk_clist_column_titles_passive(CListWidget);
  end
  else begin
    CListWidget := PGtkCList(gtk_clist_new(1));
    gtk_clist_column_titles_hide(CListWidget);
    gtk_clist_set_column_auto_resize(CListWidget, 0, True);
  end;
  gtk_clist_set_shadow_type(CListWidget, GTK_SHADOW_IN);

  gtk_container_add(PGtkContainer(ScrollWidget), PGtkWidget(CListWidget));

  gtk_widget_unset_flags(ScrollWidget^.hscrollbar, GTK_CAN_FOCUS);
  gtk_widget_unset_flags(ScrollWidget^.vscrollbar, GTK_CAN_FOCUS);
  gtk_scrolled_window_set_policy(ScrollWidget, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  // the next is not really needed since the container has only one child
  gtk_container_set_focus_vadjustment(PGtkContainer(CListWidget), gtk_scrolled_window_get_vadjustment(ScrollWidget));
  gtk_container_set_focus_hadjustment(PGtkContainer(CListWidget), gtk_scrolled_window_get_hadjustment(ScrollWidget));
  gtk_widget_show_all(PGtkWidget(CListWidget));

  // create widget info
  // already created in TGtkWSBaseScrollingWinControl
  // Replace the ScrollingInfo with our info
  WidgetInfo := GetWidgetInfo(ScrollWidget);
  WidgetInfo^.CoreWidget := PGtkWidget(CListWidget);
  ScrollingData := WidgetInfo^.UserData;
  New(ListViewData);
  ListViewData^.ScrollingData := ScrollingData^;
  ListViewData^.ViewStyle := ListView.ViewStyle;
  Dispose(ScrollingData);
  WidgetInfo^.UserData := ListViewData;
  //todo: obsolete
  // SetMainWidget(ScrollWidget, CListWidget);

  // set allocation
  // already created in TGtkWSBaseScrollingWinControl

  Set_RC_Name(AWinControl, PGtkWidget(ScrollWidget));
  SetListCallbacks(PGtkWidget(ScrollWidget), PGtkWidget(CListWidget), WidgetInfo);
end;

class procedure TGtkWSCustomListView.SetListCallbacks(const AScrollWidget, AListWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo);
begin
  TGtkWSBaseScrollingWinControl.SetCallbacks(AScrollWidget, AWidgetInfo);

  SignalConnect(AListWidget, 'click-column',        @GtkWSCustomListView_ClickColumn,       AWidgetInfo);
  SignalConnect(AListWidget, 'resize-column',       @GtkWSCustomListView_ResizeColumn,      AWidgetInfo);
  SignalConnect(AListWidget, 'abort-column-resize', @GtkWSCustomListView_AbortColumnResize, AWidgetInfo);
  // SignalConnect(AListWidget, 'row-move',            @GtkWSCustomListView_RowMove,           AWidgetInfo);
  SignalConnect(AListWidget, 'select-row',          @GtkWSCustomListView_SelectRow,         AWidgetInfo);
  SignalConnect(AListWidget, 'unselect-row',        @GtkWSCustomListView_UnSelectRow,       AWidgetInfo);
  SignalConnect(AListWidget, 'toggle-focus-row',    @GtkWSCustomListView_ToggleFocusRow,    AWidgetInfo);
  SignalConnect(AListWidget, 'select-all',          @GtkWSCustomListView_SelectAll,         AWidgetInfo);
  SignalConnect(AListWidget, 'unselect-all',        @GtkWSCustomListView_UnSelectAll,       AWidgetInfo);
  SignalConnect(AListWidget, 'end-selection',       @GtkWSCustomListView_EndSelection,      AWidgetInfo);
end;

class procedure TGtkWSCustomListView.BeginUpdate(const ALV: TCustomListView);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'BeginUpdate')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  
  gtk_clist_freeze(CListWidget);
end;

class procedure TGtkWSCustomListView.EndUpdate(const ALV: TCustomListView);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'EndUpdate')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  
  gtk_clist_thaw(CListWidget);
end;

class function TGtkWSCustomListView.GetBoundingRect(const ALV: TCustomListView): TRect;
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  Result:=Rect(0,0,0,0);
  if not WSCheckHandleAllocated(ALV, 'GetBoundingRect')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  if CListWidget=nil then exit;
  
  
  if TLVHack(ALV).ViewStyle in [vsIcon, vsSmallIcon]
  then begin
    // TODO: implement
  end
  else begin
    Result := Rect(0,0,0,0);
  end;
end;

class function TGtkWSCustomListView.GetDropTarget(const ALV: TCustomListView): Integer;
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  Result:=0;
  if not WSCheckHandleAllocated(ALV, 'GetDropTarget')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  if CListWidget=nil then exit;
  
  // TODO: implement
  Result := -1;
end;

class function TGtkWSCustomListView.GetFocused(const ALV: TCustomListView): Integer;
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'GetFocused')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  
  Result := CListWidget^.focus_row;
end;

class function TGtkWSCustomListView.GetHoverTime(const ALV: TCustomListView): Integer;
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  Result := -1; // = default
  if not WSCheckHandleAllocated(ALV, 'GetHoverTime')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  if CListWidget=nil then exit;
  
  // TODO: implement
  Result := -1; // = default
end;

class function TGtkWSCustomListView.GetItemAt(const ALV: TCustomListView; x,
  y: integer): Integer;
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
  I, FirstRowY, LastRowY: Integer;
  ScrolledTop: Integer;
  RowHeight: Integer;
  
begin
  if not WSCheckHandleAllocated(ALV, 'GetItemAt')
  then Exit(-1);

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  
  ScrolledTop := Trunc(CListWidget^.vadjustment^.value);

  FirstRowY := ScrolledTop;
  
  // For some reason the actual row height is one pixel more than the size it says
  RowHeight := CListWidget^.row_height+1;

  Dec(y, CListWidget^.column_title_area.height);

  LastRowY := FirstRowY + CListWidget^.clist_window_height;
  
  Inc(y, ScrolledTop);
  
  for I := FirstRowY div RowHeight to LastRowY div RowHeight do
  begin
    if I > CListWidget^.rows-1 then
      Exit;
    if (I * RowHeight < y) and ((I+1) * RowHeight >= y-1) then
      Exit(I);
  end;
end;

class function TGtkWSCustomListView.GetSelCount(const ALV: TCustomListView): Integer;
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'GetSelCount')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  
  if CListWidget^.selection = nil
  then Result := 0
  else Result := g_list_length(CListWidget^.selection);
end;

class function TGtkWSCustomListView.GetSelection(const ALV: TCustomListView): Integer;
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'GetSelection')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  
  if CListWidget^.selection = nil
  then Result := -1
  else Result := PtrUInt(CListWidget^.selection^.data)
end;

class function TGtkWSCustomListView.GetTopItem(const ALV: TCustomListView): Integer;
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'GetTopItem')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  
  // Result := ROW_FROM_YPIXEL(0)
  // #define ROW_FROM_YPIXEL(clist, y)  (((y) - (clist)->voffset) / \
  //                                    ((clist)->row_height + CELL_SPACING))

  Result := -CListWidget^.voffset div (CListWidget^.row_height + 1);
end;

class function TGtkWSCustomListView.GetViewOrigin(const ALV: TCustomListView): TPoint;
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'GetViewOrigin')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  if CListWidget=nil then exit;

  Result.X := Round(CListWidget^.hAdjustment^.value);
  Result.Y := Round(CListWidget^.vAdjustment^.value);
end;

class function TGtkWSCustomListView.GetVisibleRowCount(const ALV: TCustomListView): Integer;
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'GetVisibleRowCount')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  
  Result := CListWidget^.clist_window_height div (CListWidget^.row_height + 1);
end;

class procedure TGtkWSCustomListView.SetAllocBy(const ALV: TCustomListView;
  const AValue: Integer);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'SetAllocBy')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  if CListWidget=nil then exit;
  // TODO: implement ?
end;

class procedure TGtkWSCustomListView.SetDefaultItemHeight(const ALV: TCustomListView;
  const AValue: Integer);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
const
  GTK_CLIST_ROW_HEIGHT_SET = 1 shl 1;
begin
  if not WSCheckHandleAllocated(ALV, 'SetDefaultItemHeight')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);

  if (AValue = 0) and (CListWidget^.flags and GTK_CLIST_ROW_HEIGHT_SET = 0) then
    exit;
  gtk_clist_set_row_height(CListWidget, AValue);
end;

class procedure TGtkWSCustomListView.SetHotTrackStyles(const ALV: TCustomListView;
  const AValue: TListHotTrackStyles);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'SetHotTrackStyles')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  
  if CListWidget=nil then exit;
  // TODO: implement ?
end;

class procedure TGtkWSCustomListView.SetHoverTime(const ALV: TCustomListView;
  const AValue: Integer);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'SetHoverTime')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  
  if CListWidget=nil then exit;
  // TODO: implement ?
end;

class procedure TGtkWSCustomListView.SetImageList(const ALV: TCustomListView;
  const AList: TListViewImageList; const AValue: TCustomImageListResolution);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'SetImageList')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  
  if CListWidget=nil then exit;
  // TODO: implement
end;

class procedure TGtkWSCustomListView.SetPropertyInternal(
  const ACListWidget: PGtkCList; const AInfo: PWidgetInfo;
  const AProp: TListViewProperty; const AIsSet: Boolean);
var
  ListViewData: PCustomListViewData;
begin
  case AProp of
    lvpAutoArrange: begin
      // TODO: implement ??
    end;
    lvpCheckboxes: begin
      // TODO: implement
    end;
    lvpColumnClick: begin
      // allow only column modifications when in report mode
      ListViewData := AInfo^.UserData;
      if ListViewData^.ViewStyle <> vsReport then Exit;

      if AIsSet
      then gtk_clist_column_titles_active(ACListWidget)
      else gtk_clist_column_titles_passive(ACListWidget);
    end;
    lvpFlatScrollBars: begin
      // TODO: implement ??
    end;
    lvpFullDrag: begin
      // TODO: implement ??
    end;
    lvpGridLines: begin
      // TODO: implement
      // maybe possible with some cellwidget hacking
    end;
    lvpHideSelection: begin
      // TODO: implement
      // should be possible with some focus in/out events
    end;
    lvpHotTrack: begin
      // TODO: implement
      // should be possible with some mouse tracking
    end;
    lvpMultiSelect: begin
      if AIsSet
      then gtk_clist_set_selection_mode(ACListWidget, GTK_SELECTION_EXTENDED)
      else gtk_clist_set_selection_mode(ACListWidget, GTK_SELECTION_BROWSE);
    end;
    lvpOwnerDraw: begin
      // TODO: implement
      // use custom images/widgets ?
    end;
    lvpReadOnly: begin
      // TODO: implement inline editor ?
    end;
    lvpRowSelect: begin
      // TODO: implement ???
      // how to do cell select
    end;
    lvpShowColumnHeaders: begin
      // allow only column modifications when in report mode
      ListViewData := AInfo^.UserData;
      if ListViewData^.ViewStyle <> vsReport then Exit;

      if AIsSet
      then gtk_clist_column_titles_show(ACListWidget)
      else gtk_clist_column_titles_hide(ACListWidget);
    end;
    lvpShowWorkAreas: begin
      // TODO: implement ???
    end;
    lvpWrapText: begin
      // TODO: implement ???
    end;
  end;
end;

class procedure TGtkWSCustomListView.SetProperty(const ALV: TCustomListView;
  const AProp: TListViewProperty; const AIsSet: Boolean);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'SetProperty')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  
  SetPropertyInternal(CListWidget, WidgetInfo, AProp, AIsSet);
end;

class procedure TGtkWSCustomListView.SetProperties(const ALV: TCustomListView; const AProps: TListViewProperties);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
  Prop: TListViewProperty;
begin
  if not WSCheckHandleAllocated(ALV, 'SetProperties')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  
  for Prop := Low(TListViewProperty) to High(TListViewProperty) do
    SetPropertyInternal(CListWidget, WidgetInfo, Prop, Prop in AProps);
end;

class procedure TGtkWSCustomListView.SetScrollBars(const ALV: TCustomListView; const AValue: TScrollStyle);
var
  ScrollWidget: PGtkScrolledWindow;
  hPolicy, vPolicy: TGtkPolicyType;
begin
  if not WSCheckHandleAllocated(ALV, 'SetScrollBars')
  then Exit;

  ScrollWidget := PGtkScrolledWindow(ALV.Handle);
  
  case AValue of
    ssHorizontal, ssBoth: hPolicy := GTK_POLICY_ALWAYS;
    ssAutoHorizontal, ssAutoBoth: hPolicy := GTK_POLICY_AUTOMATIC;
  else
    hPolicy := GTK_POLICY_NEVER;
  end;
  
  case AValue of
    ssVertical, ssBoth: vPolicy := GTK_POLICY_ALWAYS;
    ssAutoVertical, ssAutoBoth: vPolicy := GTK_POLICY_AUTOMATIC;
  else
    vPolicy := GTK_POLICY_NEVER;
  end;

  gtk_scrolled_window_set_policy(ScrollWidget, hPolicy, vPolicy);
end;

class procedure TGtkWSCustomListView.SetSort(const ALV: TCustomListView;
  const AType: TSortType; const AColumn: Integer;
  const ASortDirection: TSortDirection);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'SetSort')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  
  if CListWidget=nil then exit;
  // TODO implement
end;

class procedure TGtkWSCustomListView.SetViewOrigin(const ALV: TCustomListView; const AValue: TPoint);
var
  WidgetInfo: PWidgetInfo;
  CListWidget: PGtkCList;
begin
  if not WSCheckHandleAllocated(ALV, 'SetViewOrigin')
  then Exit;

  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  CListWidget := PGtkCList(WidgetInfo^.CoreWidget);
  if CListWidget=nil then exit;

  gtk_adjustment_set_value(CListWidget^.hAdjustment, AValue.X);
  gtk_adjustment_set_value(CListWidget^.vAdjustment, AValue.Y);
end;

class procedure TGtkWSCustomListView.SetViewStyle(const ALV: TCustomListView; const Avalue: TViewStyle);
var
  WidgetInfo: PWidgetInfo;
  ListViewData: PCustomListViewData;
begin
  if not WSCheckHandleAllocated(ALV, 'SetViewStyle')
  then Exit;


  WidgetInfo := GetWidgetInfo(Pointer(ALV.Handle));
  ListViewData := WidgetInfo^.UserData;
  if ListViewData^.ViewStyle = AValue then Exit; // nothing to do

  // We cannot change columns or viewstyle so we need to recreate
  RecreateWnd(ALV);
end;

