(*
    GTK-Kylix Library: GTKView - GtkTreeView and its modifications
    Version 0.6.21  (last updated 2006-05-06)
    Copyright (C) 2006 Tomas Bzatek <tbzatek@users.sourceforge.net>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the 
    Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
    Boston, MA  02111-1307  USA.

*)

unit GTKView;
{$O+}   //  Optimization needs to be enabled for this unit due to bug in compiler (fixed columns doesn't work)

interface

uses gtk2, gdk2, gdk2pixbuf, glib2, Classes, GTKControls, GTKConsts, GTKUtils, GTKClasses, GTKForms, GTKPixbuf;
   //  Quick jump: QForms QControls QStdCtrls QComCtrls


type
  TGTKListItem = class;
  TGTKTreeViewColumn = class;

(****************************************** TGTKTREEVIEWCOLUMN ******************************************************************)
  TGTKTreeViewColumnSizingMode = (smGrowOnly, smAutoSize, smFixed);
  TGTKTreeViewSortOrder = (soAscending, soDescending, soNone);
  TGTKTreeViewColumnType = (ctText, ctToggle, ctImageText);
  TGTKTreeViewColumnEditedEvent = procedure(Sender: TObject; Column: TGTKTreeViewColumn; Item: TGTKListItem; var NewText: string; var AllowChange: boolean; var DataColumn: integer) of object;
  TGTKTreeViewColumnToggledEvent = procedure(Sender: TObject; Column: TGTKTreeViewColumn; Item: TGTKListItem) of object;
  TGTKTreeViewColumn = class(TComponent)
  private
    FOwner: TComponent;
    FColumnType: TGTKTreeViewColumnType;
    FOnEdited: TGTKTreeViewColumnEditedEvent;
    FOnToggled: TGTKTreeViewColumnToggledEvent;
    FOnClicked: TNotifyEvent;
    FIndex: integer;
    function GetCaption: string;
    function GetVisible: boolean;
    function GetResizable: boolean;
    function GetSizingMode: TGTKTreeViewColumnSizingMode;
    function GetWidth: integer;
    function GetFixedWidth: integer;
    function GetMinWidth: integer;
    function GetMaxWidth: integer;
    function GetClickable: boolean;
    function GetAlignment: Double;
    function GetReorderable: boolean;
    function GetSortID: integer;
    function GetShowSortIndicator: boolean;
    function GetSortOrder: TGTKTreeViewSortOrder;
    procedure SetCaption(Value: string);
    procedure SetVisible(Value: boolean);
    procedure SetResizable(Value: boolean);
    procedure SetSizingMode(Value: TGTKTreeViewColumnSizingMode);
    procedure SetFixedWidth(Value: integer);
    procedure SetMinWidth(Value: integer);
    procedure SetMaxWidth(Value: integer);
    procedure SetClickable(Value: boolean);
    procedure SetAlignment(Value: Double);
    procedure SetReorderable(Value: boolean);
    procedure SetSortID(Value: integer);
    procedure SetShowSortIndicator(Value: boolean);
    procedure SetSortOrder(Value: TGTKTreeViewSortOrder);
    procedure SetColumnType(Value: TGTKTreeViewColumnType);
  public
    FColumn: PGtkTreeViewColumn;
    FRenderer, FPixbufRenderer: PGtkCellRenderer;
    constructor Create(AOwner: TComponent); override;
    constructor CreateTyped(AOwner: TComponent; ColType: TGTKTreeViewColumnType);
    destructor Destroy; override;
    procedure AddAttribute(Attribute: string; Value: integer);
    procedure SetProperty(AProperty: string; Value: integer); overload;
    procedure SetProperty(AProperty: string; Value: string); overload;
    procedure SetProperty(AProperty: string; Value: Double); overload;
    procedure SetProperty(AProperty: string; Value: pointer); overload;
    procedure AddImageAttribute(Attribute: string; Value: integer);
    procedure SetImageProperty(AProperty: string; Value: integer); overload;
    procedure SetImageProperty(AProperty: string; Value: string); overload;
    procedure SetImageProperty(AProperty: string; Value: Double); overload;
    procedure SetImageProperty(AProperty: string; Value: pointer); overload;
    procedure ClearAttributes;
    property Parent: TComponent read FOwner;
  published
    property Caption: string read GetCaption write SetCaption;
    property Visible: boolean read GetVisible write SetVisible;
    property Resizable: boolean read GetResizable write SetResizable;
    property SizingMode: TGTKTreeViewColumnSizingMode read GetSizingMode write SetSizingMode;
    property Width: integer read GetWidth;
    property FixedWidth: integer read GetFixedWidth write SetFixedWidth;
    property MinWidth: integer read GetMinWidth write SetMinWidth;
    property MaxWidth: integer read GetMaxWidth write SetMaxWidth;
    property Clickable: boolean read GetClickable write SetClickable;
    property Alignment: Double read GetAlignment write SetAlignment;
    property Reorderable: boolean read GetReorderable write SetReorderable;
    property SortID: integer read GetSortID write SetSortID;
    property ShowSortIndicator: boolean read GetShowSortIndicator write SetShowSortIndicator;
    property SortOrder: TGTKTreeViewSortOrder read GetSortOrder write SetSortOrder;
    property ColumnType: TGTKTreeViewColumnType read FColumnType write SetColumnType;
    property Index: integer read FIndex;
    property OnEdited: TGTKTreeViewColumnEditedEvent read FOnEdited write FOnEdited;
    property OnToggled: TGTKTreeViewColumnToggledEvent read FOnToggled write FOnToggled;
    property OnClicked: TNotifyEvent read FOnClicked write FOnClicked;
  end;

(****************************************** TGTKTREEVIEWCOLUMNS *****************************************************************)
  TGTKTreeViewColumns = class(TComponent)
  private
    FOwner: TComponent;
    FList: TList;
    function GetCount: Integer;
    function GetItem(Index: Integer): TGTKTreeViewColumn;
    procedure SetItem(Index: Integer; Value: TGTKTreeViewColumn);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function Add: TGTKTreeViewColumn;
    function AddTyped(ColType: TGTKTreeViewColumnType): TGTKTreeViewColumn;
    procedure Insert(Index: Integer; Item: TGTKTreeViewColumn);
    procedure Delete(Index: Integer);
    procedure Clear;
    procedure AutosizeColumns;
    property Items[Index: Integer]: TGTKTreeViewColumn read GetItem write SetItem; default;
    property Parent: TComponent read FOwner;
  published
    property Count: Integer read GetCount;
  end;

(****************************************** TGTKLISTITEM ************************************************************************)
  TGTKListItem = class(TComponent)
  private
    FOwner: TComponent;
    FIndex: longint;
    FData: pointer;
    function GetSelected: boolean;
    procedure SetSelected(Value: boolean);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function AsString(Index: longint): string;
    function AsInteger(Index: longint): integer;
    function AsPointer(Index: longint): pointer;
    function AsBoolean(Index: longint): boolean;
    procedure SetValue(Index: longint; Value: string); overload;
    procedure SetValue(Index: longint; Value: integer); overload;
    procedure SetValue(Index: longint; Value: pointer); overload;
    procedure SetValue(Index: longint; Value: boolean); overload;
    procedure SetCursor(const FocusColumnNo: integer; const StartEditing, UseAlignment: boolean; const AlignX, AlignY: Double);
    procedure StartEditing(ColumnNo: integer);
    procedure RedrawRow;
    property Data: pointer read FData write FData;
    property Parent: TComponent read FOwner;
    function IsVisible: boolean;
  published
    property Selected: boolean read GetSelected write SetSelected;
    property Index: integer read FIndex;
  end;

(****************************************** TGTKLISTITEMS ***********************************************************************)
  TGTKListColumnType = (lcText, lcNumber, lcPointer, lcPixbuf, lcBoolean);
  TGTKListItems = class(TComponent)
  private
    FStore: PGtkListStore;
    FOwner: TComponent;
    FList: TList;
    function GetCount: longint;
    function GetItem(Index: longint): TGTKListItem;
    procedure SetItem(Index: longint; Value: TGTKListItem);
  public
    constructor Create(AOwner: TComponent); override;
    constructor CreateTyped(AOwner: TComponent; ColumnTypes: array of TGTKListColumnType);
    destructor Destroy; override;
    procedure SetColumnTypes(Types: array of TGTKListColumnType);
    function Add: TGTKListItem;
    procedure Insert(Index: longint; Item: TGTKListItem);
    procedure Append(Item: TGTKListItem);
    procedure Delete(Index: longint);
    procedure Clear;
    property Items[Index: longint]: TGTKListItem read GetItem write SetItem; default;
    property Parent: TComponent read FOwner;
  published
    property Count: longint read GetCount;
  end;

(****************************************** TGTKVIEW ****************************************************************************)
  TGTKSelectionMode = (smNone, smSingle, smBrowse, smMultiple);
  TGTKCellDataFunc = procedure (Sender: TObject; tree_view: PGtkTreeView; tree_column : PGtkTreeViewColumn; cell : PGtkCellRenderer; tree_model : PGtkTreeModel; iter : PGtkTreeIter) of object;
  TGTKSortCoversionFunc = function (Sender: TObject; const Index: integer): integer of object;
  TGTKView = class(TGTKContainer)
  private
    FColumns: TGTKTreeViewColumns;
    FOnSelectionChanged: TNotifyEvent;
    FTreeModelSort: PGtkTreeModelSort;
    FCellDataFunc: TGTKCellDataFunc;
    FTreeModel: PGtkTreeModel;
    FToSortedConvFunc, FFromSortedConvFunc: TGTKSortCoversionFunc;
    function GetSelectionMode: TGTKSelectionMode;
    function GetShowHeaders: boolean;
    function GetRulesHint: boolean;
    function GetReorderable: boolean;
    function GetEnableSearch: boolean;
    function GetSearchColumn: integer;
    function GetSortColumnID: integer;
    function GetSortOrder: TGTKTreeViewSortOrder;
    procedure SetSelectionMode(Value: TGTKSelectionMode);
    procedure SetShowHeaders(Value: boolean);
    procedure SetRulesHint(Value: boolean);
    procedure SetReorderable(Value: boolean);
    procedure SetEnableSearch(Value: boolean);
    procedure SetSearchColumn(Value: integer);
    procedure SetSortColumnID(Value: integer);
    procedure SetSortOrder(Value: TGTKTreeViewSortOrder);
  protected
  public
    FSelection: PGtkTreeSelection;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure SelectAll;
    procedure DeselectAll;
    procedure SetSortInfo(ColumnID: integer; Order: TGTKTreeViewSortOrder);
    function ConvertToSorted(Index: integer): integer;
    function ConvertFromSorted(Index: integer): integer;
    procedure ConvertPathToChild(var Path: PGtkTreePath);
    procedure ConvertChildToPath(var Path: PGtkTreePath);
  published
    property Columns: TGTKTreeViewColumns read FColumns write FColumns;
    property SelectionMode: TGTKSelectionMode read GetSelectionMode write SetSelectionMode;
    property OnSelectionChanged: TNotifyEvent read FOnSelectionChanged write FOnSelectionChanged;
    property ShowHeaders: boolean read GetShowHeaders write SetShowHeaders;
    property RulesHint: boolean read GetRulesHint write SetRulesHint;
    property Reorderable: boolean read GetReorderable write SetReorderable;
    property EnableSearch: boolean read GetEnableSearch write SetEnableSearch;
    property SearchColumn: integer read GetSearchColumn write SetSearchColumn;
    property SortColumnID: integer read GetSortColumnID write SetSortColumnID;
    property SortOrder: TGTKTreeViewSortOrder read GetSortOrder write SetSortOrder;
    property CellDataFunc: TGTKCellDataFunc read FCellDataFunc write FCellDataFunc;
    property FromSortedCoversionFunc: TGTKSortCoversionFunc read FFromSortedConvFunc write FFromSortedConvFunc;
    property ToSortedCoversionFunc: TGTKSortCoversionFunc read FToSortedConvFunc write FToSortedConvFunc;
  end;

(****************************************** TGTKLISTVIEW ************************************************************************)
  TGTKLVItemActivateEvent = procedure (Sender: TObject; Item: TGTKListItem) of object;
  TGTKTreeViewCompareFunc = function (Sender: TObject; var model: PGtkTreeModel; var a, b: PGtkTreeIter): integer of object;
  TGTKListView = class(TGTKView)
  private
    FOnItemActivate: TGTKLVItemActivateEvent;
    FSortable: boolean;
    FCompareFunc: TGTKTreeViewCompareFunc;
    FOnColumnsChanged: TNotifyEvent;
  protected
    FListItems: TGTKListItems;
    procedure Recreate;
    function GetSelected: TGTKListItem;
    procedure SetSelected(Value: TGTKListItem);
  public
    constructor Create(AOwner: TComponent); override;
    constructor CreateTyped(AOwner: TComponent; Sortable: boolean; ColumnTypes: array of TGTKListColumnType);
    destructor Destroy; override;
    procedure StartEditing(ColumnNo: integer);
    function GetItemAtPos(X, Y: integer): TGTKListItem;
  published
    property Items: TGTKListItems read FListItems write FListItems;
    property Selected: TGTKListItem read GetSelected write SetSelected;
    property OnItemActivate: TGTKLVItemActivateEvent read FOnItemActivate write FOnItemActivate;
    property CompareFunc: TGTKTreeViewCompareFunc read FCompareFunc write FCompareFunc;
    property OnColumnsChanged: TNotifyEvent read FOnColumnsChanged write FOnColumnsChanged;
  end;


(********************************************************************************************************************************)
(********************************************************************************************************************************)
implementation

uses SysUtils, DateUtils;


(********************************************************************************************************************************)
(********************************************************************************************************************************)
constructor TGTKView.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FColumns := TGTKTreeViewColumns.Create(Self);
  FOnSelectionChanged := nil;
  FToSortedConvFunc := nil;
  FFromSortedConvFunc := nil;
end;

destructor TGTKView.Destroy;
begin
  FColumns.Free;
  inherited Destroy;
end;

function TGTKView.GetSelectionMode: TGTKSelectionMode;
begin
  Result := TGTKSelectionMode(gtk_tree_selection_get_mode(FSelection));
end;

procedure TGTKView.SetSelectionMode(Value: TGTKSelectionMode);
begin
  gtk_tree_selection_set_mode(FSelection, Integer(Value));
end;

procedure TGTKView.SelectAll;
begin
  gtk_tree_selection_select_all(FSelection);
end;

procedure TGTKView.DeselectAll;
begin
  gtk_tree_selection_unselect_all(FSelection);
end;

function TGTKView.GetShowHeaders: boolean;
begin
  Result := gtk_tree_view_get_headers_visible(PGtkTreeView(FWidget)); 
end;

procedure TGTKView.SetShowHeaders(Value: boolean);
begin
  gtk_tree_view_set_headers_visible(PGtkTreeView(FWidget), Value);
end;

function TGTKView.GetRulesHint: boolean;
begin
  Result := gtk_tree_view_get_rules_hint(PGtkTreeView(FWidget));
end;

procedure TGTKView.SetRulesHint(Value: boolean);
begin
  gtk_tree_view_set_rules_hint(PGtkTreeView(FWidget), Value);
end;

function TGTKView.GetReorderable: boolean;
begin
  Result := gtk_tree_view_get_reorderable(PGtkTreeView(FWidget));
end;

procedure TGTKView.SetReorderable(Value: boolean);
begin
  gtk_tree_view_set_reorderable(PGtkTreeView(FWidget), Value);
end;

function TGTKView.GetEnableSearch: boolean;
begin
  Result := gtk_tree_view_get_enable_search(PGtkTreeView(FWidget));
end;

procedure TGTKView.SetEnableSearch(Value: boolean);
begin
  gtk_tree_view_set_enable_search(PGtkTreeView(FWidget), Value);
end;

function TGTKView.GetSearchColumn: integer;
begin
  Result := gtk_tree_view_get_search_column(PGtkTreeView(FWidget));
end;

procedure TGTKView.SetSearchColumn(Value: integer);
begin
  gtk_tree_view_set_search_column(PGtkTreeView(FWidget), Value);
end;

function TGTKView.GetSortColumnID: integer;
var sort_column_id: gint;
    order: TGtkSortType;
begin
  Result := -1;
  if (FTreeModelSort <> nil) and gtk_tree_sortable_get_sort_column_id(PGtkTreeSortable(FTreeModelSort), @sort_column_id, @order)
    then Result := sort_column_id;
end;

procedure TGTKView.SetSortColumnID(Value: integer);
begin
  if FTreeModelSort <> nil then
    gtk_tree_sortable_set_sort_column_id(PGtkTreeSortable(FTreeModelSort), Value, TGtkSortType(GetSortOrder));
end;

procedure TGTKView.ConvertChildToPath(var Path: PGtkTreePath);
var NewPath: PGtkTreePath;
begin
  if Assigned(FTreeModelSort) then
    if not Assigned(FToSortedConvFunc) then begin
      NewPath := gtk_tree_model_sort_convert_child_path_to_path(FTreeModelSort, Path);
      Path := NewPath;
    end else begin
      NewPath := gtk_tree_path_new_from_string(PChar(IntToStr(FToSortedConvFunc(Self, StrToIntDef(gtk_tree_path_to_string(Path), 0)))));
      Path := NewPath;
    end;
end;

procedure TGTKView.ConvertPathToChild(var Path: PGtkTreePath);
var NewPath: PGtkTreePath;
begin
  if Assigned(FTreeModelSort) then
    if not Assigned(FFromSortedConvFunc) then begin
      NewPath := gtk_tree_model_sort_convert_path_to_child_path(FTreeModelSort, Path);
      Path := NewPath;
    end else begin
      NewPath := gtk_tree_path_new_from_string(PChar(IntToStr(FFromSortedConvFunc(Self, StrToIntDef(gtk_tree_path_to_string(Path), 0)))));
      Path := NewPath;
    end;
end;

function TGTKView.GetSortOrder: TGTKTreeViewSortOrder;
var sort_column_id: gint;
    order: TGtkSortType;
begin
  Result := soNone;
  if (FTreeModelSort <> nil) and gtk_tree_sortable_get_sort_column_id(PGtkTreeSortable(FTreeModelSort), @sort_column_id, @order)
    then Result := TGTKTreeViewSortOrder(order);
end;

procedure TGTKView.SetSortOrder(Value: TGTKTreeViewSortOrder);
begin
  if FTreeModelSort <> nil then
    gtk_tree_sortable_set_sort_column_id(PGtkTreeSortable(FTreeModelSort), GetSortColumnID, TGtkSortType(Value));
end;

procedure TGTKView.SetSortInfo(ColumnID: integer; Order: TGTKTreeViewSortOrder);
begin
  if FTreeModelSort <> nil then
    gtk_tree_sortable_set_sort_column_id(PGtkTreeSortable(FTreeModelSort), ColumnID, TGtkSortType(Order));
end;

function TGTKView.ConvertToSorted(Index: integer): integer;
var Path: PGtkTreePath;
    Iter: TGtkTreeIter;
begin
  Result := Index;
  if Application.GTKVersion_2_0_5_Up then begin
    Path := gtk_tree_path_new_from_string(StringToPgchar(IntToStr(Index)));
    if not Assigned(Path) then Exit;
    if Assigned(FTreeModelSort) then Path := gtk_tree_model_sort_convert_child_path_to_path(FTreeModelSort, Path);
    if not Assigned(Path) then Exit;
    Result := gtk_tree_path_get_indices(Path)^;
    gtk_tree_path_free(Path);
  end else
    if Assigned(FToSortedConvFunc) then Result := FToSortedConvFunc(Self, Index) else begin
      gtk_tree_model_get_iter_from_string(FTreeModel, @Iter, PChar(IntToStr(Index)));
//      gtk_tree_model_sort_convert_child_iter_to_iter(FTreeModelSort, @NewIter, @Iter);
      Path := gtk_tree_model_get_path(FTreeModel, @Iter);
      if not Assigned(Path) then Exit;
      if Assigned(FTreeModelSort) then Path := gtk_tree_model_sort_convert_child_path_to_path(FTreeModelSort, Path);
      if not Assigned(Path) then Exit;
      Result := StrToIntDef(String(gtk_tree_path_to_string(Path)), Index);
      gtk_tree_path_free(Path);
    end;
end;

function TGTKView.ConvertFromSorted(Index: integer): integer;
var Path: PGtkTreePath;
    Iter: TGtkTreeIter;
begin
  Result := Index;
  if Application.GTKVersion_2_0_5_Up then begin
    Path := gtk_tree_path_new_from_string(StringToPgchar(IntToStr(Index)));
    if not Assigned(Path) then Exit;
    if Assigned(FTreeModelSort) then Path := gtk_tree_model_sort_convert_path_to_child_path(FTreeModelSort, Path);
    if not Assigned(Path) then Exit;
    Result := gtk_tree_path_get_indices(Path)^;
    gtk_tree_path_free(Path);
  end else
    if Assigned(FFromSortedConvFunc) then Result := FFromSortedConvFunc(Self, Index) else begin
      gtk_tree_model_get_iter_from_string(FTreeModel, @Iter, PChar(IntToStr(Index)));
//      gtk_tree_model_sort_convert_child_iter_to_iter(FTreeModelSort, @NewIter, @Iter);
      Path := gtk_tree_model_get_path(FTreeModel, @Iter);
      if not Assigned(Path) then Exit;
      if Assigned(FTreeModelSort) then Path := gtk_tree_model_sort_convert_path_to_child_path(FTreeModelSort, Path);
      if not Assigned(Path) then Exit;
      Result := StrToIntDef(String(gtk_tree_path_to_string(Path)), Index);
      gtk_tree_path_free(Path);
    end;
end;

procedure CellDataFunc(tree_column : PGtkTreeViewColumn; cell : PGtkCellRenderer; tree_model : PGtkTreeModel; iter : PGtkTreeIter; data : gpointer); cdecl;
begin
  if Assigned(TGTKView(data).FCellDataFunc) then TGTKView(data).FCellDataFunc(TGTKView(data), PGtkTreeView(TGTKView(data).FWidget), tree_column, cell, tree_model, iter);
end;


(********************************************************************************************************************************)
(********************************************************************************************************************************)
constructor TGTKListView.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FListItems := TGTKListItems.Create(Self);
  FSortable := False;
  FCompareFunc := nil;
  Recreate;
end;

destructor TGTKListView.Destroy;
begin
  FListItems.Free;
  inherited Destroy;
end;

procedure TGTKListView_changed(treeselection: PGtkTreeSelection; user_data: gpointer); cdecl;
begin
  if Assigned(TGTKListView(user_data).FOnSelectionChanged) then TGTKListView(user_data).FOnSelectionChanged(TGTKListView(user_data));
end;

procedure TGTKListView_row_activated(treeview: PGtkTreeView; arg1: PGtkTreePath; arg2: PGtkTreeViewColumn; user_data: gpointer); cdecl;
var Item: TGTKListItem;
    AIndex: integer;
begin
  Item := nil;
  if Assigned(arg1) then begin
    TGTKListView(user_data).ConvertPathToChild(arg1);
    if Application.GTKVersion_2_0_5_Up then AIndex := gtk_tree_path_get_indices(arg1)^
                                       else AIndex := StrToIntDef(String(gtk_tree_path_to_string(arg1)), 0);
    Item := TGTKListView(user_data).Items[AIndex];
    Item.FIndex := AIndex;
  end;
  if Assigned(TGTKListView(user_data).FOnItemActivate) then TGTKListView(user_data).FOnItemActivate(TGTKListView(user_data), Item);
end;

procedure TGTKListView_columns_changed(treeselection: PGtkTreeSelection; user_data: gpointer); cdecl;
begin
  if Assigned(TGTKListView(user_data).FOnColumnsChanged) then TGTKListView(user_data).FOnColumnsChanged(TGTKListView(user_data));
end;

procedure TGTKListView.Recreate;
begin
  if Assigned(FWidget) then gtk_widget_destroy(PGtkWidget(FWidget));
  FTreeModelSort := nil;
  if not FSortable then FWidget := gtk_tree_view_new_with_model(PGtkTreeModel(FListItems.FStore))
  else begin
         FTreeModelSort := gtk_tree_model_sort_new_with_model(FListItems.FStore);
         FWidget := gtk_tree_view_new_with_model(FTreeModelSort);
       end;
  FSelection := gtk_tree_view_get_selection(PGtkTreeView(FWidget));
  g_signal_connect(FSelection, 'changed', G_CALLBACK(@TGTKListView_changed), Self);
  g_signal_connect(FWidget, 'row-activated', G_CALLBACK(@TGTKListView_row_activated), Self);
  g_signal_connect_after(FWidget, 'columns-changed', G_CALLBACK(@TGTKListView_columns_changed), Self);
  FTreeModel := gtk_tree_view_get_model(PGtkTreeView(FWidget));
  Show;
end;

constructor TGTKListView.CreateTyped(AOwner: TComponent; Sortable: boolean; ColumnTypes: array of TGTKListColumnType);
begin
  inherited Create(AOwner);
  FListItems := TGTKListItems.CreateTyped(Self, ColumnTypes);
  FSortable := Sortable;
  FCompareFunc := nil;
  Recreate;
end;

function TGTKListView.GetSelected: TGTKListItem;
var Iter{, NewIter}: TGtkTreeIter;
    Path: PGtkTreePath;
    AIndex: integer;
    i: integer;
begin
  Result := nil;
  Path := nil;
  if not Application.GTKVersion_2_0_5_Up then begin
    for i := 0 to Items.Count - 1 do begin
      Path := gtk_tree_path_new_from_string(PChar(IntToStr(i)));
      if gtk_tree_selection_path_is_selected(gtk_tree_view_get_selection(PGtkTreeView(FWidget)), Path) then Break else
//      WriteLn('Index ', i, ', Selected: ', gtk_tree_selection_path_is_selected(gtk_tree_view_get_selection(PGtkTreeView(FWidget)), Path));
      gtk_tree_path_free(Path);
      Path := nil;
    end;
//    WriteLn('Selected: ', String(gtk_tree_path_to_string(Path)));
    ConvertPathToChild(Path);
(*    gtk_tree_model_get_iter_from_string(gtk_tree_view_get_model(PGtkTreeView(FWidget)){FTreeModelSort}, @Iter, gtk_tree_path_to_string(Path));
    gtk_tree_model_sort_convert_child_iter_to_iter(FTreeModelSort, @NewIter, @Iter);
    Path := gtk_tree_model_get_path({FTreeModelSort}gtk_tree_view_get_model(PGtkTreeView(FWidget)), @NewIter);
 *)
//    gtk_tree_model_sort_clear_cache(FTreeModelSort);
//    Path := gtk_tree_model_sort_convert_path_to_child_path(FTreeModelSort, Path);
  end else begin
    if not gtk_tree_selection_get_selected(gtk_tree_view_get_selection(PGtkTreeView(FWidget)), nil, @Iter) then Exit;
    Path := gtk_tree_model_get_path(gtk_tree_view_get_model(PGtkTreeView(FWidget)), @iter);
    if not Assigned(Path) then Exit;
    ConvertPathToChild(Path);
  end;
//  WriteLn('Selected Converted: ', String(gtk_tree_path_to_string(Path)));
  if Assigned(Path) then begin
    if Application.GTKVersion_2_0_5_Up then AIndex := gtk_tree_path_get_indices(Path)^
                                       else AIndex := StrToIntDef(String(gtk_tree_path_to_string(Path)), 0);
    Result := Items[AIndex];
    Result.FIndex := AIndex;
    gtk_tree_path_free(Path);
  end;
end;

procedure TGTKListView.SetSelected(Value: TGTKListItem);
var Path: PGtkTreePath;
begin
  Path := gtk_tree_path_new_from_string(StringToPgchar(IntToStr(Value.FIndex)));
  ConvertChildToPath(Path);
  gtk_tree_selection_select_path(FSelection, Path);
  gtk_tree_path_free(Path);
end;

function GtkTreeIterCompareFunc(model: PGtkTreeModel; a: PGtkTreeIter; b: PGtkTreeIter; user_data: gpointer): gint; cdecl;
var Value: TGValue;
    s: string;
begin
  if not Assigned(TGTKListView(user_data).FCompareFunc) then begin
    Value.g_type := 0;
    gtk_tree_model_get_value(model, a, TGTKView(user_data).GetSortColumnID, @Value);
    s := string(PChar(Value.data[0].v_pointer));
    Value.g_type := 0;
    gtk_tree_model_get_value(model, b, TGTKView(user_data).GetSortColumnID, @Value);
    Result := AnsiCompareStr(s, PChar(Value.data[0].v_pointer));
  end else Result := TGTKListView(user_data).FCompareFunc(TGTKView(user_data), model, a, b);
end;

procedure TGTKListView.StartEditing(ColumnNo: integer);
var Iter: TGtkTreeIter;
    Path: PGtkTreePath;
begin
  if not gtk_tree_selection_get_selected(FSelection, nil, @Iter) then Exit;
  Path := gtk_tree_model_get_path(FTreeModel, @iter);
  if not Assigned(Path) then Exit;
  gtk_tree_view_set_cursor(PGtkTreeView(FWidget), Path, FColumns[ColumnNo].FColumn, True);
end;

function TGTKListView.GetItemAtPos(X, Y: integer): TGTKListItem;
var Path: PGtkTreePath;
    Column: PGtkTreeViewColumn;
    AIndex: integer;
begin
  Result := nil;
  if gtk_tree_view_get_path_at_pos(PGtkTreeView(FWidget), X, Y, Path, Column, nil, nil) then begin
    if not Assigned(Path) then Exit;
    ConvertPathToChild(Path);
    if Application.GTKVersion_2_0_5_Up then AIndex := gtk_tree_path_get_indices(Path)^
                                       else AIndex := StrToIntDef(String(gtk_tree_path_to_string(Path)), 0);
    Result := Items[AIndex];
    Result.FIndex := AIndex;
    gtk_tree_path_free(Path);
  end;
end;


(********************************************************************************************************************************)
(********************************************************************************************************************************)
constructor TGTKListItems.Create(AOwner: TComponent);
const Types : GType = G_TYPE_STRING;
begin
  inherited Create(AOwner);
  FOwner := AOwner;
  FStore := gtk_list_store_newv(1, @Types);
  FList := TList.Create;
end;

destructor TGTKListItems.Destroy;
begin
  if not (csDestroying in ComponentState) then begin
    Clear;
    FList.Free;
    gtk_object_destroy(PGtkObject(FStore));
  end;
  inherited Destroy;
end;

constructor TGTKListItems.CreateTyped(AOwner: TComponent; ColumnTypes: array of TGTKListColumnType);
var i: integer;
    Cols : array[0..255] of GType;
begin
  inherited Create(AOwner);
  FOwner := AOwner;
  FList := TList.Create;
  for i := Low(ColumnTypes) to High(ColumnTypes) do
    case ColumnTypes[i] of
      lcText : Cols[i] := G_TYPE_STRING;
      lcNumber : Cols[i] := G_TYPE_LONG;
      lcPointer : Cols[i] := G_TYPE_POINTER;
      lcPixbuf : Cols[i] := GDK_TYPE_PIXBUF;
      lcBoolean : Cols[i] := G_TYPE_BOOLEAN;
    end;
  FStore := gtk_list_store_newv(Length(ColumnTypes), @Cols);
end;

procedure TGTKListItems.SetColumnTypes(Types: array of TGTKListColumnType);
var i: integer;
    Cols : array[0..255] of GType;
begin
  for i := Low(Types) to High(Types) do
    case Types[i] of
      lcText : Cols[i] := G_TYPE_STRING;
      lcNumber : Cols[i] := G_TYPE_LONG;
      lcPointer : Cols[i] := G_TYPE_POINTER;
      lcPixbuf : Cols[i] := GDK_TYPE_PIXBUF;
      lcBoolean : Cols[i] := G_TYPE_BOOLEAN;
    end;
  g_object_unref(PGObject(FStore));
  FStore := gtk_list_store_newv(Length(Types), @Cols);
  (FOwner as TGTKListView).Recreate;
end;

function TGTKListItems.GetItem(Index: longint): TGTKListItem;
begin
  Result := TGTKListItem(FList[Index]);
  Result.FIndex := Index;
end;

procedure TGTKListItems.SetItem(Index: longint; Value: TGTKListItem);
begin
  FList[Index] := Value;
end;

function TGTKListItems.Add: TGTKListItem;
begin
  Result := TGTKListItem.Create(Self);
  Result.FIndex := GetCount;
//  Insert(GetCount, Result);
  Append(Result);
end;

function TGTKListItems.GetCount: longint;
begin
  Result := FList.Count;
end;

procedure TGTKListItems.Clear;
var i: longint;
begin
  for i := Count - 1 downto 0 do Delete(i);   //  !!!! DON'T use here gtk_list_store_clear(FStore), because it causes memory leaks !!!!
end;

procedure TGTKListItems.Delete(Index: longint);
var x: pointer;
    Iter: TGtkTreeIter;
begin
  x := FList[Index];
  if gtk_tree_model_get_iter_from_string(PGtkTreeModel(FStore), @Iter, StringToPgchar(IntToStr(Index))) then begin
//    (FOwner as TGTKView).CovertSortableIter(Iter);
    gtk_list_store_remove(FStore, @Iter);
  end;
  FList.Delete(Index);
  TObject(x).Free;
end;

procedure TGTKListItems.Insert(Index: longint; Item: TGTKListItem);
var Iter: TGtkTreeIter;
begin
  FList.Insert(Index, Item);
  gtk_list_store_insert(FStore, @Iter, Index);
end;

procedure TGTKListItems.Append(Item: TGTKListItem);
var Iter: TGtkTreeIter;
begin
  FList.Add(Item);
  gtk_list_store_append(FStore, @Iter);
end;

(********************************************************************************************************************************)
(********************************************************************************************************************************)
constructor TGTKTreeViewColumns.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FOwner := AOwner;
  FList := TList.Create;
end;

destructor TGTKTreeViewColumns.Destroy;
begin
  if not (csDestroying in ComponentState) then begin
    Clear;
    FList.Free;
  end;
  inherited Destroy;
end;

function TGTKTreeViewColumns.GetItem(Index: Integer): TGTKTreeViewColumn;
begin
  Result := TGTKTreeViewColumn(FList[Index]);
  Result.FIndex := Index;
end;

procedure TGTKTreeViewColumns.SetItem(Index: Integer; Value: TGTKTreeViewColumn);
begin
  FList[Index] := Value;
end;

function TGTKTreeViewColumns.Add: TGTKTreeViewColumn;
begin
  Result := TGTKTreeViewColumn.Create(Self);
  Result.FIndex := GetCount;
  Insert(GetCount, Result);
end;

function TGTKTreeViewColumns.AddTyped(ColType: TGTKTreeViewColumnType): TGTKTreeViewColumn;
begin
  Result := TGTKTreeViewColumn.CreateTyped(Self, ColType);
  Result.FIndex := GetCount;
  Insert(GetCount, Result);
end;

function TGTKTreeViewColumns.GetCount: Integer;
begin
  Result := FList.Count;
end;

procedure TGTKTreeViewColumns.Clear;
var i: Integer;
begin
  for i := Count - 1 downto 0 do Delete(i);
end;

procedure TGTKTreeViewColumns.Delete(Index: Integer);
var x : pointer;
begin
  if not (csDestroying in ComponentState) then
    gtk_tree_view_remove_column(PGtkTreeView((FOwner as TGTKControl).FWidget), TGTKTreeViewColumn(FList[Index]).FColumn);
  x := FList[Index];
  FList.Delete(Index);
  TObject(x).Free;
end;

procedure TGTKTreeViewColumns.Insert(Index: Integer; Item: TGTKTreeViewColumn);
begin
  FList.Insert(Index, Item);
  Item.FIndex := Index;
  gtk_tree_view_insert_column(PGtkTreeView((FOwner as TGTKControl).FWidget), Item.FColumn, Index);
  if Assigned((FOwner as TGTKView).FTreeModelSort) then
    gtk_tree_sortable_set_sort_func(PGtkTreeSortable((FOwner as TGTKView).FTreeModelSort), Index, TGtkTreeIterCompareFunc(@GtkTreeIterCompareFunc), FOwner, nil);
  gtk_tree_view_column_set_cell_data_func(Item.FColumn, Item.FRenderer, @CellDataFunc, (FOwner as TGTKView), nil);
  if Item.ColumnType = ctImageText then gtk_tree_view_column_set_cell_data_func(Item.FColumn, Item.FPixbufRenderer, @CellDataFunc, (FOwner as TGTKView), nil);
end;

procedure TGTKTreeViewColumns.AutosizeColumns;
begin
  gtk_tree_view_columns_autosize(PGtkTreeView((FOwner as TGTKControl).FWidget));
end;

(********************************************************************************************************************************)
(********************************************************************************************************************************)
constructor TGTKTreeViewColumn.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FOwner := AOwner;
  FColumn := gtk_tree_view_column_new;
  FRenderer := nil;
  FPixbufRenderer := nil;
  FOnEdited := nil;
  FOnToggled := nil;
  FOnClicked := nil;
  SetColumnType(ctText);
end;

constructor TGTKTreeViewColumn.CreateTyped(AOwner: TComponent; ColType: TGTKTreeViewColumnType);
begin
  inherited Create(AOwner);
  FOwner := AOwner;
  FColumn := gtk_tree_view_column_new;
  FRenderer := nil;
  FPixbufRenderer := nil;
  FOnEdited := nil;
  FOnToggled := nil;
  FOnClicked := nil;
  SetColumnType(ColType);
end;

destructor TGTKTreeViewColumn.Destroy;
begin
  if not (csDestroying in ComponentState) then begin
    gtk_object_destroy(PGtkObject(FRenderer));
    if GTK_IS_OBJECT(FColumn) then gtk_object_destroy(PGtkObject(FColumn));
  end;
  inherited Destroy;
end;

function TGTKTreeViewColumn.GetCaption: string;
begin
  Result := PgcharToString(gtk_tree_view_column_get_title(FColumn));
end;

procedure TGTKTreeViewColumn.SetCaption(Value: string);
begin
  gtk_tree_view_column_set_title(FColumn, StringToPgchar(Value));
end;

function TGTKTreeViewColumn.GetVisible: boolean;
begin
  Result := gtk_tree_view_column_get_visible(FColumn);
end;

procedure TGTKTreeViewColumn.SetVisible(Value: boolean);
begin
  gtk_tree_view_column_set_visible(FColumn, Value);
end;

function TGTKTreeViewColumn.GetResizable: boolean;
begin
  Result := gtk_tree_view_column_get_resizable(FColumn);
end;

procedure TGTKTreeViewColumn.SetResizable(Value: boolean);
begin
  gtk_tree_view_column_set_resizable(FColumn, Value);
end;

function TGTKTreeViewColumn.GetSizingMode: TGTKTreeViewColumnSizingMode;
begin
  Result := TGTKTreeViewColumnSizingMode(gtk_tree_view_column_get_sizing(FColumn));
end;

procedure TGTKTreeViewColumn.SetSizingMode(Value: TGTKTreeViewColumnSizingMode);
begin
  gtk_tree_view_column_set_sizing(FColumn, TGtkTreeViewColumnSizing(Value));
end;

function TGTKTreeViewColumn.GetWidth: integer;
begin
  Result := gtk_tree_view_column_get_width(FColumn);
end;

function TGTKTreeViewColumn.GetFixedWidth: integer;
begin
  Result := gtk_tree_view_column_get_fixed_width(FColumn);
end;

procedure TGTKTreeViewColumn.SetFixedWidth(Value: integer);
begin
  gtk_tree_view_column_set_fixed_width(FColumn, Value);
end;

function TGTKTreeViewColumn.GetMinWidth: integer;
begin
  Result := gtk_tree_view_column_get_min_width(FColumn);
end;

procedure TGTKTreeViewColumn.SetMinWidth(Value: integer);
begin
  gtk_tree_view_column_set_min_width(FColumn, Value);
end;

function TGTKTreeViewColumn.GetMaxWidth: integer;
begin
  Result := gtk_tree_view_column_get_max_width(FColumn);
end;

procedure TGTKTreeViewColumn.SetMaxWidth(Value: integer);
begin
  gtk_tree_view_column_set_max_width(FColumn, Value);
end;

function TGTKTreeViewColumn.GetClickable: boolean;
begin
  Result := gtk_tree_view_column_get_clickable(FColumn);
end;

procedure TGTKTreeViewColumn.SetClickable(Value: boolean);
begin
  gtk_tree_view_column_set_clickable(FColumn, Value);
end;

function TGTKTreeViewColumn.GetAlignment: Double;
begin
  Result := gtk_tree_view_column_get_alignment(FColumn);
end;

procedure TGTKTreeViewColumn.SetAlignment(Value: Double);
begin
  gtk_tree_view_column_set_alignment(FColumn, Value);
end;

function TGTKTreeViewColumn.GetReorderable: boolean;
begin
  Result := gtk_tree_view_column_get_reorderable(FColumn);
end;

procedure TGTKTreeViewColumn.SetReorderable(Value: boolean);
begin
  gtk_tree_view_column_set_reorderable(FColumn, Value);
end;

function TGTKTreeViewColumn.GetSortID: integer;
begin
  Result := gtk_tree_view_column_get_sort_column_id(FColumn);
end;

procedure TGTKTreeViewColumn.SetSortID(Value: integer);
begin
  gtk_tree_view_column_set_sort_column_id(FColumn, Value);
end;

function TGTKTreeViewColumn.GetShowSortIndicator: boolean;
begin
  Result := gtk_tree_view_column_get_sort_indicator(FColumn);
end;

procedure TGTKTreeViewColumn.SetShowSortIndicator(Value: boolean);
begin
  gtk_tree_view_column_set_sort_indicator(FColumn, Value);
end;

function TGTKTreeViewColumn.GetSortOrder: TGTKTreeViewSortOrder;
begin
  Result := TGTKTreeViewSortOrder(gtk_tree_view_column_get_sort_order(FColumn));
end;

procedure TGTKTreeViewColumn.SetSortOrder(Value: TGTKTreeViewSortOrder);
begin
  gtk_tree_view_column_set_sort_order(FColumn, TGtkSortType(Value));
end;

procedure TGTKTreeViewColumn_edited(cell: PGtkCellRendererText; const path_string, new_text: pgchar; data: gpointer); cdecl;
var NewText: string;
    AllowChange: boolean;
    DataColumn, AIndex: integer;
    Path: PGtkTreePath;
begin
  if Assigned(TGTKTreeViewColumn(data).FOnEdited) then begin
    NewText := PgcharToString(new_text);
    AllowChange := True;
    DataColumn := TGTKTreeViewColumn(data).FIndex;
    Path := gtk_tree_path_new_from_string(path_string);
    if not Assigned(Path) then Exit;
    if Application.GTKVersion_2_0_5_Up then AIndex := gtk_tree_path_get_indices(Path)^
                                       else AIndex := StrToIntDef(String(gtk_tree_path_to_string(Path)), 0);
    TGTKTreeViewColumn(data).FOnEdited(TGTKTreeViewColumn(data), TGTKTreeViewColumn(data), ((TGTKTreeViewColumn(data).FOwner as TGTKTreeViewColumns).FOwner as TGTKListView).Items[AIndex], NewText, AllowChange, DataColumn);
    if AllowChange and (((TGTKTreeViewColumn(data).FOwner as TGTKTreeViewColumns).FOwner as TGTKListView).Items.Count < AIndex) then
      ((TGTKTreeViewColumn(data).FOwner as TGTKTreeViewColumns).FOwner as TGTKListView).Items[AIndex].SetValue(DataColumn, NewText);
    gtk_tree_path_free(Path);
  end;
end;

procedure TGTKTreeViewColumn_toggled(cell: PGtkCellRendererToggle; const path_string: pgchar; data: gpointer); cdecl;
var Path: PGtkTreePath;
    AIndex: integer;
begin
  if Assigned(data) and Assigned(TGTKTreeViewColumn(data).FOnToggled) then begin
    Path := gtk_tree_path_new_from_string(path_string);
    if not Assigned(Path) then Exit;
    if Application.GTKVersion_2_0_5_Up then AIndex := gtk_tree_path_get_indices(Path)^
                                       else AIndex := StrToIntDef(String(gtk_tree_path_to_string(Path)), 0);
    TGTKTreeViewColumn(data).FOnToggled(TGTKTreeViewColumn(data), TGTKTreeViewColumn(data), ((TGTKTreeViewColumn(data).FOwner as TGTKTreeViewColumns).FOwner as TGTKListView).Items[AIndex]);
  end;
end;

procedure TGTKTreeViewColumn_clicked(treeviewcolumn: PGtkTreeViewColumn; user_data: gpointer); cdecl;
begin
  if Assigned(user_data) and Assigned(TGTKTreeViewColumn(user_data).FOnClicked) then
    TGTKTreeViewColumn(user_data).FOnClicked(TGTKTreeViewColumn(user_data));
end;

procedure TGTKTreeViewColumn.SetColumnType(Value: TGTKTreeViewColumnType);
begin
  FColumnType := Value;
  if Assigned(FRenderer) then begin
    gtk_tree_view_column_clear(FColumn);
    gtk_object_destroy(PGtkObject(FRenderer));
  end;
  case Value of
    ctText : FRenderer := gtk_cell_renderer_text_new;
    ctToggle : FRenderer := gtk_cell_renderer_toggle_new;
    ctImageText : begin
                    FRenderer := gtk_cell_renderer_text_new;
                    FPixbufRenderer := gtk_cell_renderer_pixbuf_new;
                  end;
  end;
  if Value = ctImageText then gtk_tree_view_column_pack_start(FColumn, FPixbufRenderer, False);
  gtk_tree_view_column_pack_start(FColumn, FRenderer, True);
  if (Value = ctImageText) or (Value = ctText) then g_signal_connect(FRenderer, 'edited', G_CALLBACK(@TGTKTreeViewColumn_edited), Self);
  if Value = ctToggle then g_signal_connect(FRenderer, 'toggled', G_CALLBACK(@TGTKTreeViewColumn_toggled), Self);
  g_signal_connect_after(FColumn, 'clicked', G_CALLBACK(@TGTKTreeViewColumn_clicked), Self);
end;

procedure TGTKTreeViewColumn.AddAttribute(Attribute: string; Value: integer);
begin
  gtk_tree_view_column_add_attribute(FColumn, FRenderer, StringToPgchar(Attribute), Value);
end;

procedure TGTKTreeViewColumn.AddImageAttribute(Attribute: string; Value: integer);
begin
  gtk_tree_view_column_add_attribute(FColumn, FPixbufRenderer, StringToPgchar(Attribute), Value);
end;

procedure TGTKTreeViewColumn.ClearAttributes;
begin
  gtk_tree_view_column_clear_attributes(FColumn, FRenderer);
end;

procedure TGTKTreeViewColumn.SetProperty(AProperty: string; Value: integer);
var AValue: TGValue;
begin
  AValue.g_type := G_TYPE_LONG;
  AValue.data[0].v_long := Value;
  g_object_set_property(PGObject(FRenderer), StringToPgchar(AProperty), @AValue);
end;

procedure TGTKTreeViewColumn.SetProperty(AProperty: string; Value: string);
var AValue: TGValue;
begin
  AValue.g_type := G_TYPE_STRING;
  AValue.data[0].v_pointer := StringToPgchar(Value);
  g_object_set_property(PGObject(FRenderer), StringToPgchar(AProperty), @AValue);
end;

procedure TGTKTreeViewColumn.SetProperty(AProperty: string; Value: Double);
var AValue: TGValue;
begin
  AValue.g_type := G_TYPE_DOUBLE;
  AValue.data[0].v_double := Value;
  g_object_set_property(PGObject(FRenderer), StringToPgchar(AProperty), @AValue);
end;

procedure TGTKTreeViewColumn.SetProperty(AProperty: string; Value: pointer);
var AValue: TGValue;
begin
  AValue.g_type := G_TYPE_POINTER;
  AValue.data[0].v_pointer := Value;
  g_object_set_property(PGObject(FRenderer), StringToPgchar(AProperty), @AValue);
end;

procedure TGTKTreeViewColumn.SetImageProperty(AProperty: string; Value: integer);
var AValue: TGValue;
begin
  AValue.g_type := G_TYPE_LONG;
  AValue.data[0].v_long := Value;
  g_object_set_property(PGObject(FPixbufRenderer), StringToPgchar(AProperty), @AValue);
end;

procedure TGTKTreeViewColumn.SetImageProperty(AProperty: string; Value: string);
var AValue: TGValue;
begin
  AValue.g_type := G_TYPE_STRING;
  AValue.data[0].v_pointer := StringToPgchar(Value);
  g_object_set_property(PGObject(FPixbufRenderer), StringToPgchar(AProperty), @AValue);
end;

procedure TGTKTreeViewColumn.SetImageProperty(AProperty: string; Value: Double);
var AValue: TGValue;
begin
  AValue.g_type := G_TYPE_DOUBLE;
  AValue.data[0].v_double := Value;
  g_object_set_property(PGObject(FPixbufRenderer), StringToPgchar(AProperty), @AValue);
end;

procedure TGTKTreeViewColumn.SetImageProperty(AProperty: string; Value: pointer);
var AValue: TGValue;
begin
  AValue.g_type := G_TYPE_POINTER;
  AValue.data[0].v_pointer := Value;
  g_object_set_property(PGObject(FPixbufRenderer), StringToPgchar(AProperty), @AValue);
end;

(********************************************************************************************************************************)
(********************************************************************************************************************************)
constructor TGTKListItem.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FOwner := AOwner;
  FData := nil;
end;

destructor TGTKListItem.Destroy;
begin
  inherited Destroy;
end;

function TGTKListItem.AsString(Index: longint): string;
var Iter: TGtkTreeIter;
    AValue: TGValue;
begin
  Result := '';
  if gtk_tree_model_get_iter_from_string(PGtkTreeModel((FOwner as TGTKListItems).FStore), @Iter, StringToPgchar(IntToStr(FIndex))) then
  begin
    AValue.g_type := 0;
    gtk_tree_model_get_value(PGtkTreeModel((FOwner as TGTKListItems).FStore), @Iter, Index, @AValue);
    Result := PgcharToString(AValue.data[0].v_pointer);
  end;
end;

function TGTKListItem.AsInteger(Index: longint): integer;
var Iter: TGtkTreeIter;
    AValue: TGValue;
begin
  Result := 0;
  if gtk_tree_model_get_iter_from_string(PGtkTreeModel((FOwner as TGTKListItems).FStore), @Iter, StringToPgchar(IntToStr(FIndex))) then
  begin
    AValue.g_type := 0;
    gtk_tree_model_get_value(PGtkTreeModel((FOwner as TGTKListItems).FStore), @Iter, Index, @AValue);
    Result := AValue.data[0].v_long;
  end;
end;

function TGTKListItem.AsPointer(Index: longint): pointer;
var Iter: TGtkTreeIter;
    AValue: TGValue;
begin
  Result := nil;
  if gtk_tree_model_get_iter_from_string(PGtkTreeModel((FOwner as TGTKListItems).FStore), @Iter, StringToPgchar(IntToStr(FIndex))) then
  begin
    AValue.g_type := 0;
    gtk_tree_model_get_value(PGtkTreeModel((FOwner as TGTKListItems).FStore), @Iter, Index, @AValue);
    Result := AValue.data[0].v_pointer;
  end;
end;

function TGTKListItem.AsBoolean(Index: longint): boolean;
var Iter: TGtkTreeIter;
    AValue: TGValue;
begin
  Result := False;
  if gtk_tree_model_get_iter_from_string(PGtkTreeModel((FOwner as TGTKListItems).FStore), @Iter, StringToPgchar(IntToStr(FIndex))) then
  begin
    AValue.g_type := 0;
    gtk_tree_model_get_value(PGtkTreeModel((FOwner as TGTKListItems).FStore), @Iter, Index, @AValue);
    Result := boolean(AValue.data[0].v_int);
  end;
end;

procedure TGTKListItem.SetValue(Index: longint; Value: string);
var Iter: TGtkTreeIter;
begin
  if gtk_tree_model_get_iter_from_string(PGtkTreeModel((FOwner as TGTKListItems).FStore), @Iter, StringToPgchar(IntToStr(FIndex)))
    then gtk_list_store_set(PGtkTreeModel((FOwner as TGTKListItems).FStore), @Iter, Index, StringToPgchar(Value), -1); 
end;

procedure TGTKListItem.SetValue(Index: longint; Value: integer);
var Iter: TGtkTreeIter;
begin
  if gtk_tree_model_get_iter_from_string(PGtkTreeModel((FOwner as TGTKListItems).FStore), @Iter, StringToPgchar(IntToStr(FIndex)))
    then gtk_list_store_set(PGtkTreeModel((FOwner as TGTKListItems).FStore), @Iter, Index, Value, -1);
end;

procedure TGTKListItem.SetValue(Index: longint; Value: pointer);
var Iter: TGtkTreeIter;
begin
  if gtk_tree_model_get_iter_from_string(PGtkTreeModel((FOwner as TGTKListItems).FStore), @Iter, StringToPgchar(IntToStr(FIndex)))
    then gtk_list_store_set(PGtkTreeModel((FOwner as TGTKListItems).FStore), @Iter, Index, Value, -1);
end;

procedure TGTKListItem.SetValue(Index: longint; Value: boolean);
var Iter: TGtkTreeIter;
begin
  if gtk_tree_model_get_iter_from_string(PGtkTreeModel((FOwner as TGTKListItems).FStore), @Iter, StringToPgchar(IntToStr(FIndex)))
    then gtk_list_store_set(PGtkTreeModel((FOwner as TGTKListItems).FStore), @Iter, Index, Value, -1);
end;

function TGTKListItem.GetSelected: boolean;
var Iter: TGtkTreeIter;
begin
  Result := False;
  if gtk_tree_model_get_iter_from_string(PGtkTreeModel((FOwner as TGTKListItems).FStore), @Iter, StringToPgchar(IntToStr(FIndex))) then begin
//    ((FOwner as TGTKListItems).FOwner as TGTKView).ConvertSortableIter(Iter);
    Result := gtk_tree_selection_iter_is_selected(((FOwner as TGTKListItems).FOwner as TGTKView).FSelection, @Iter);
  end;
end;

procedure TGTKListItem.SetSelected(Value: boolean);
var Iter, NewIter: TGtkTreeIter;
    Path: PGtkTreePath;
begin
  try
    if not Application.GTKVersion_2_0_5_Up then begin
      Path := gtk_tree_path_new_from_string(StringToPgchar(IntToStr(FIndex)));
      ((FOwner as TGTKListItems).FOwner as TGTKView).ConvertChildToPath(Path);
      gtk_tree_selection_select_path(((FOwner as TGTKListItems).FOwner as TGTKView).FSelection, Path);
      gtk_tree_path_free(Path);
    end else
    if gtk_tree_model_get_iter_from_string(PGtkTreeModel((FOwner as TGTKListItems).FStore), @Iter, StringToPgchar(IntToStr(FIndex))) then begin
      if Assigned(((FOwner as TGTKListItems).FOwner as TGTKView).FTreeModelSort) then begin
        gtk_tree_model_sort_convert_child_iter_to_iter(((FOwner as TGTKListItems).FOwner as TGTKView).FTreeModelSort, @NewIter, @Iter);
        Iter := NewIter;
      end;
      if Value then gtk_tree_selection_select_iter(((FOwner as TGTKListItems).FOwner as TGTKView).FSelection, @Iter)
               else gtk_tree_selection_unselect_iter(((FOwner as TGTKListItems).FOwner as TGTKView).FSelection, @Iter);
    end;
  except end;
end;

procedure TGTKListItem.SetCursor(const FocusColumnNo: integer; const StartEditing, UseAlignment: boolean; const AlignX, AlignY: Double);
var Path: PGtkTreePath;
    Column: PGtkTreeViewColumn;
//    Renderer: PGtkCellRenderer;
    i : integer;
begin
  Path := gtk_tree_path_new_from_string(StringToPgchar(IntToStr(FIndex)));
  if not Assigned(Path) then Exit;
  ((FOwner as TGTKListItems).FOwner as TGTKView).ConvertChildToPath(Path);
  if not Assigned(Path) then Exit;

  Column := ((FOwner as TGTKListItems).FOwner as TGTKView).Columns[FocusColumnNo].FColumn;
//  Renderer := ((FOwner as TGTKListItems).FOwner as TGTKView).Columns[FocusColumnNo].FRenderer;

//  if not Application.GTKVersion_2_0_5_Up then
    for i := 1 to 2 do gtk_main_iteration_do(False);   //  This ugly piece of code HAVE TO BE HERE due some focus-related problems in GtkTreeView
  gtk_tree_view_set_cursor(PGtkTreeView(((FOwner as TGTKListItems).FOwner as TGTKView).FWidget), Path, Column, StartEditing);
//  gtk_tree_view_set_cursor(PGtkTreeView(((FOwner as TGTKListItems).FOwner as TGTKView).FWidget), Path, Column, StartEditing);
//  gtk_tree_view_set_cursor_on_cell(PGtkTreeView(((FOwner as TGTKListItems).FOwner as TGTKView).FWidget), Path, Column, Renderer, StartEditing);
  gtk_tree_view_scroll_to_cell(PGtkTreeView(((FOwner as TGTKListItems).FOwner as TGTKView).FWidget), Path, nil, UseAlignment, AlignX, AlignY);

  gtk_tree_path_free(Path);
end;

procedure TGTKListItem.StartEditing(ColumnNo: integer);
var CellEditable: PGtkCellEditable;
    Path: PGtkTreePath;
    Column: PGtkTreeViewColumn;
    BackgroundRect, CellRect: TGdkRectangle;
begin
  Path := gtk_tree_path_new_from_string(StringToPgchar(IntToStr(FIndex)));
  if not Assigned(Path) then Exit;
  ((FOwner as TGTKListItems).FOwner as TGTKView).ConvertChildToPath(Path);
  if not Assigned(Path) then Exit;
  Column := ((FOwner as TGTKListItems).FOwner as TGTKView).Columns[ColumnNo].FColumn;
  gtk_tree_view_get_background_area(PGtkTreeView(((FOwner as TGTKListItems).FOwner as TGTKView).FWidget), Path, Column, @BackgroundRect);
  gtk_tree_view_get_cell_area(PGtkTreeView(((FOwner as TGTKListItems).FOwner as TGTKView).FWidget), Path, Column, @CellRect);

  gtk_tree_view_column_focus_cell(Column, ((FOwner as TGTKListItems).FOwner as TGTKView).Columns[ColumnNo].FRenderer);
  CellEditable := gtk_cell_renderer_start_editing(((FOwner as TGTKListItems).FOwner as TGTKView).Columns[ColumnNo].FRenderer,
                    nil, ((FOwner as TGTKListItems).FOwner as TGTKView).FWidget, PChar(StringToPgchar(IntToStr(FIndex))),
                    @BackgroundRect, @CellRect, {GTK_CELL_RENDERER_SELECTED or GTK_CELL_RENDERER_SORTED} 0);
  gtk_widget_show(CellEditable);
//  gtk_cell_editable_start_editing(CellEditable, nil);


  gtk_tree_path_free(Path);
end;

procedure TGTKListItem.RedrawRow;
var Rect, BackgroundRect: TGdkRectangle;
    Path: PGtkTreePath;
    Column: PGtkTreeViewColumn;
begin
  Path := gtk_tree_path_new_from_string(StringToPgchar(IntToStr(FIndex)));
  if not Assigned(Path) then Exit;
  ((FOwner as TGTKListItems).FOwner as TGTKView).ConvertChildToPath(Path);
  if not Assigned(Path) then Exit;
  Column := ((FOwner as TGTKListItems).FOwner as TGTKView).Columns[0].FColumn;
  gtk_tree_view_get_background_area(PGtkTreeView(((FOwner as TGTKListItems).FOwner as TGTKView).FWidget), Path, Column, @BackgroundRect);

  Rect.x := 0;
  Rect.width := ((FOwner as TGTKListItems).FOwner as TGTKView).FWidget^.allocation.width;
  Rect.y := BackgroundRect.y;
  Rect.height := BackgroundRect.height;
  gdk_window_invalidate_rect(gtk_tree_view_get_bin_window(PGtkTreeView(((FOwner as TGTKListItems).FOwner as TGTKView).FWidget)), @Rect, True);

  gtk_tree_path_free(Path);
end;

function TGTKListItem.IsVisible: boolean;
var CellRect, VisibleRect: TGdkRectangle;
    Path: PGtkTreePath;
begin
  Result := False;
  gtk_tree_view_get_visible_rect(PGtkTreeView(((FOwner as TGTKListItems).FOwner as TGTKView).FWidget), @VisibleRect);
  if not Assigned(Path) then Exit;
  Path := gtk_tree_path_new_from_string(StringToPgchar(IntToStr(FIndex)));
  ((FOwner as TGTKListItems).FOwner as TGTKView).ConvertChildToPath(Path);
  if not Assigned(Path) then Exit;
  gtk_tree_view_get_background_area(PGtkTreeView(((FOwner as TGTKListItems).FOwner as TGTKView).FWidget), Path, nil, @CellRect);
  gtk_tree_path_free(Path);
  Result := (CellRect.y > VisibleRect.y) and (CellRect.y + CellRect.height < VisibleRect.y + VisibleRect.height);
end;


(********************************************************************************************************************************)
(********************************************************************************************************************************)


end.
