{ $Id$ ***************************************************************************** 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. ***************************************************************************** } unit Gtk2ListViewTreeModel; {$mode objfpc}{$H+} interface uses // FPC Classes, SysUtils, // Gtk gtk2, glib2, // LCL ComCtrls; const ELCLListViewModel = 'LCLListViewModel'; type { TLCLTreeModel } PLCLListViewModel = ^TLCLListViewModel; { TLCLListViewModel } TLCLListViewModel = object {%REGION GObject} // Add nothing in or before this region!! g_type_instance : TGTypeInstance; ref_count : guint; qdata : PGData; {%ENDREGION} {%REGION Interface methods in order!} procedure row_changed({%H-}path:PGtkTreePath; {%H-}iter:PGtkTreeIter); cdecl; procedure row_inserted({%H-}path:PGtkTreePath; {%H-}iter:PGtkTreeIter); cdecl; procedure row_has_child_toggled({%H-}path:PGtkTreePath; {%H-}iter:PGtkTreeIter); cdecl; procedure row_deleted({%H-}path:PGtkTreePath); cdecl; procedure rows_reordered({%H-}path:PGtkTreePath; {%H-}iter:PGtkTreeIter; {%H-}new_order:Pgint); cdecl; function get_flags():TGtkTreeModelFlags; cdecl; function get_n_columns():gint; cdecl; function get_column_type({%H-}index:gint):GType; cdecl; function get_iter(iter:PGtkTreeIter; path:PGtkTreePath):gboolean; cdecl; function get_path(iter:PGtkTreeIter):PGtkTreePath; cdecl; procedure get_value(iter:PGtkTreeIter; {%H-}column:gint; value:PGValue); cdecl; function iter_next(iter:PGtkTreeIter):gboolean; cdecl; function iter_children({%H-}iter:PGtkTreeIter; {%H-}parent:PGtkTreeIter):gboolean; cdecl; function iter_has_child({%H-}iter:PGtkTreeIter):gboolean; cdecl; function iter_n_children(iter:PGtkTreeIter):gint; cdecl; function iter_nth_child(iter:PGtkTreeIter; parent:PGtkTreeIter; n:gint):gboolean; cdecl; function iter_parent({%H-}iter:PGtkTreeIter; {%H-}child:PGtkTreeIter):gboolean; cdecl; procedure ref_node({%H-}iter:PGtkTreeIter); cdecl; procedure unref_node({%H-}iter:PGtkTreeIter); cdecl; {%ENDREGION} procedure NotifyRowInserted(AIndex: PtrUInt); procedure NotifyRowDeleted(AIndex: PtrUInt); function TreeModel: PGtkTreeModel; inline; public ListView: TCustomListView; end; PMyTreeModelObjectClass = ^TLCLTreeModelClass; TLCLTreeModelClass = record parent_class: TGObjectClass; end; TLVItemHack = class(TListItem) end; function LCLListViewModelNew(AListView: TCustomListView): PLCLListViewModel; function LCLLISTVIEW_MODEL_TYPE: GType; procedure LCLListViewModelClassInit({%H-}g_class:gpointer; {%H-}class_data:gpointer); cdecl; procedure LCLListViewModelInit({%H-}instance:PGTypeInstance; {%H-}g_class:gpointer); cdecl; procedure LCLListViewModelInterfaceInit(g_iface:PGtkTreeModelIface; {%H-}iface_data:gpointer); cdecl; implementation var _LCLLISTVIEW_MODEL_TYPE: gulong = 0; function LCLListViewModelNew(AListView: TCustomListView): PLCLListViewModel; begin Result := g_object_new(LCLLISTVIEW_MODEL_TYPE, nil); Result^.ListView := AListView; end; function LCLLISTVIEW_MODEL_TYPE: GType; var TypeInfo: TGTypeInfo; INterfaceINfo: TGInterfaceInfo; begin _LCLLISTVIEW_MODEL_TYPE := g_type_from_name(ELCLListViewModel); if _LCLLISTVIEW_MODEL_TYPE = 0 then begin with TypeInfo do begin class_size:= SizeOf(TLCLTreeModelClass); base_init := nil;//TGBaseInitFunc; base_finalize:= nil;// TGBaseFinalizeFunc; class_init := @LCLListViewModelClassInit;// TGClassInitFunc; class_finalize := nil;// TGClassFinalizeFunc; class_data := nil;// gconstpointer; instance_size := SizeOf(TLCLListViewModel);// guint16; n_preallocs := 0;// guint16; instance_init := @LCLListViewModelInit;// TGInstanceInitFunc; value_table := nil;// PGTypeValueTable; end; with InterfaceInfo do begin interface_init := TGInterfaceInitFunc(@LCLListViewModelInterfaceInit);// TGInterfaceInitFunc; interface_finalize := nil; //TGInterfaceFinalizeFunc; interface_data := nil;//gpointer; end; _LCLLISTVIEW_MODEL_TYPE := g_type_register_static(G_TYPE_OBJECT, ELCLListViewModel, @TypeInfo, 0); g_type_add_interface_static(_LCLLISTVIEW_MODEL_TYPE, GTK_TYPE_TREE_MODEL, @InterfaceInfo); end; Result := _LCLLISTVIEW_MODEL_TYPE; end; procedure LCLListViewModelClassInit(g_class:gpointer; class_data:gpointer); cdecl; begin end; procedure LCLListViewModelInit(instance:PGTypeInstance; g_class:gpointer); cdecl; begin end; procedure LCLListViewModelInterfaceInit(g_iface:PGtkTreeModelIface; iface_data:gpointer); cdecl; var P: PPointer; begin // Do not change the order here!! P := PPointer(@g_iface^.row_changed); P^ := @TLCLListViewModel.row_changed; Inc(P); P^ := @TLCLListViewModel.row_inserted; Inc(P); P^ := @TLCLListViewModel.row_has_child_toggled; Inc(P); P^ := @TLCLListViewModel.row_deleted; Inc(P); P^ := @TLCLListViewModel.rows_reordered; Inc(P); P^ := @TLCLListViewModel.get_flags; Inc(P); P^ := @TLCLListViewModel.get_n_columns; Inc(P); P^ := @TLCLListViewModel.get_column_type; Inc(P); P^ := @TLCLListViewModel.get_iter; Inc(P); P^ := @TLCLListViewModel.get_path; Inc(P); P^ := @TLCLListViewModel.get_value; Inc(P); P^ := @TLCLListViewModel.iter_next; Inc(P); P^ := @TLCLListViewModel.iter_children; Inc(P); P^ := @TLCLListViewModel.iter_has_child; Inc(P); P^ := @TLCLListViewModel.iter_n_children; Inc(P); P^ := @TLCLListViewModel.iter_nth_child; Inc(P); P^ := @TLCLListViewModel.iter_parent; Inc(P); P^ := @TLCLListViewModel.ref_node; Inc(P); P^ := @TLCLListViewModel.unref_node; Inc(P); end; { TLCLListViewModel } procedure TLCLListViewModel.row_changed(path: PGtkTreePath; iter: PGtkTreeIter); cdecl; begin end; procedure TLCLListViewModel.row_inserted(path: PGtkTreePath; iter: PGtkTreeIter); cdecl; begin end; procedure TLCLListViewModel.row_has_child_toggled(path: PGtkTreePath; iter: PGtkTreeIter); cdecl; begin end; procedure TLCLListViewModel.row_deleted(path: PGtkTreePath); cdecl; begin end; procedure TLCLListViewModel.rows_reordered(path: PGtkTreePath; iter: PGtkTreeIter; new_order: Pgint); cdecl; begin end; function TLCLListViewModel.get_flags(): TGtkTreeModelFlags; cdecl; begin Result := GTK_TREE_MODEL_LIST_ONLY or 0; end; function TLCLListViewModel.get_n_columns(): gint; cdecl; begin Result := 1; end; function TLCLListViewModel.get_column_type(index: gint): GType; cdecl; begin Result := G_TYPE_POINTER; end; function TLCLListViewModel.get_iter(iter: PGtkTreeIter; path: PGtkTreePath): gboolean; cdecl; var Index: PtrUInt; begin Result := False; Index := gtk_tree_path_get_indices(path)[0]; if Index < ListView.Items.Count then begin iter^.user_data:= {%H-}Pointer(Index); Exit(True); end; end; function TLCLListViewModel.get_path(iter: PGtkTreeIter): PGtkTreePath; cdecl; var Index: PtrUint; begin Result := nil; if iter = nil then Exit; Index := {%H-}PtrUint(Iter^.user_data); Result := gtk_tree_path_new_from_indices(Index, -1); end; procedure TLCLListViewModel.get_value(iter: PGtkTreeIter; column: gint; value: PGValue); cdecl; var Index: Integer; Item: TLVItemHack; //ValueType: GType; //SubIndex: Integer; begin Index := {%H-}PtrUint(Iter^.user_data); Item := TLVItemHack(ListView.Items.Item[Index]); g_value_init(value, G_TYPE_POINTER); g_value_set_pointer(value, Pointer(Item)); // We use custom renderers so the below is not needed and was never tested :) { Listview Columns in the tree model are stored like so [ 0: Checked. 1: Pixbuf. 2: Caption. 3: Subitem(x) Pixbuf. 4: Subitem(x) Text etc } { Case column of 0: begin g_value_init(value, G_TYPE_BOOLEAN); g_value_set_boolean(value, Item.GetCheckedInternal); end; 1: begin g_value_init(value, GTK_TYPE_PIXMAP); //g_value_set_pointer(ListView.im); !! g_value_set_pointer(value, nil); end; 2: begin g_value_init(value, G_TYPE_STRING); g_value_set_static_string(value,PChar(Item.Caption)); end; else SubIndex := (column - 3) div 2; if Column and 1 = 0 then // Picbuf begin g_value_init(value, GTK_TYPE_PIXMAP); //g_value_set_pointer(ListView.im); !! g_value_set_pointer(value, nil); end else // Text; begin g_value_init(value, G_TYPE_STRING); if SubIndex >= Item.SubItems.Count then g_value_set_static_string(value,PChar('')) else g_value_set_static_string(value,PChar(Item.SubItems.Strings[SubIndex])); end; end; } end; function TLCLListViewModel.iter_next(iter: PGtkTreeIter): gboolean; cdecl; begin Result := False; if ListView = nil then Exit; Inc({%H-}PtrUInt(Iter^.user_data)); Result := {%H-}PtrUint(Iter^.user_data) < ListView.items.Count; end; function TLCLListViewModel.iter_children(iter: PGtkTreeIter; parent: PGtkTreeIter): gboolean; cdecl; begin Result := False; end; function TLCLListViewModel.iter_has_child(iter: PGtkTreeIter): gboolean; cdecl; begin Result := false; end; function TLCLListViewModel.iter_n_children(iter: PGtkTreeIter): gint; cdecl; begin Result := 0; if (Iter = nil) and (ListView <> nil) then Result := ListView.Items.Count; end; function TLCLListViewModel.iter_nth_child(iter: PGtkTreeIter; parent: PGtkTreeIter; n: gint): gboolean; cdecl; begin Result := False; if (ListView = nil) or (parent <> nil) then Exit; if (Iter <> nil) and (n < ListView.Items.Count) then begin {%H-}PtrUint(Iter^.user_data) := n; Result := True; end; end; function TLCLListViewModel.iter_parent(iter: PGtkTreeIter; child: PGtkTreeIter): gboolean; cdecl; begin Result := False; end; procedure TLCLListViewModel.ref_node(iter: PGtkTreeIter); cdecl; begin end; procedure TLCLListViewModel.unref_node(iter: PGtkTreeIter); cdecl; begin end; procedure TLCLListViewModel.NotifyRowInserted(AIndex: PtrUInt); var Path: PGtkTreePath; Iter: TGtkTreeIter; begin Iter.user_data := {%H-}Pointer(AIndex); path := gtk_tree_path_new_from_indices(AIndex, -1); //emits a signal gtk_tree_model_row_inserted(TreeModel, path, @iter); gtk_tree_path_free(path); end; procedure TLCLListViewModel.NotifyRowDeleted(AIndex: PtrUInt); var Path: PGtkTreePath; begin path := gtk_tree_path_new_from_indices(AIndex, -1); //emits a signal gtk_tree_model_row_deleted(TreeModel, path); gtk_tree_path_free(path); end; function TLCLListViewModel.TreeModel: PGtkTreeModel; inline; begin Result := PGtkTreeModel(@Self); end; end.