1 { $Id$
2 
3  *****************************************************************************
4   This file is part of the Lazarus Component Library (LCL)
5 
6   See the file COPYING.modifiedLGPL.txt, included in this distribution,
7   for details about the license.
8  *****************************************************************************
9 }
10 
11 unit Gtk2ListViewTreeModel;
12 
13 {$mode objfpc}{$H+}
14 
15 interface
16 
17 uses
18   // FPC
19   Classes, SysUtils,
20   // Gtk
21   gtk2, glib2,
22   // LCL
23   ComCtrls;
24 const
25   ELCLListViewModel = 'LCLListViewModel';
26 type
27 
28   { TLCLTreeModel }
29   PLCLListViewModel = ^TLCLListViewModel;
30 
31   { TLCLListViewModel }
32 
33   TLCLListViewModel = object
34     {%REGION GObject} // Add nothing in or before this region!!
35     g_type_instance : TGTypeInstance;
36     ref_count : guint;
37     qdata : PGData;
38     {%ENDREGION}
39     {%REGION Interface methods in order!}
40     procedure row_changed({%H-}path:PGtkTreePath; {%H-}iter:PGtkTreeIter); cdecl;
41     procedure row_inserted({%H-}path:PGtkTreePath; {%H-}iter:PGtkTreeIter); cdecl;
42     procedure row_has_child_toggled({%H-}path:PGtkTreePath; {%H-}iter:PGtkTreeIter); cdecl;
43     procedure row_deleted({%H-}path:PGtkTreePath); cdecl;
44     procedure rows_reordered({%H-}path:PGtkTreePath; {%H-}iter:PGtkTreeIter; {%H-}new_order:Pgint); cdecl;
get_flagsnull45     function  get_flags():TGtkTreeModelFlags; cdecl;
get_n_columnsnull46     function  get_n_columns():gint; cdecl;
get_column_typenull47     function  get_column_type({%H-}index:gint):GType; cdecl;
get_iternull48     function  get_iter(iter:PGtkTreeIter; path:PGtkTreePath):gboolean; cdecl;
get_pathnull49     function  get_path(iter:PGtkTreeIter):PGtkTreePath; cdecl;
50     procedure get_value(iter:PGtkTreeIter; {%H-}column:gint; value:PGValue); cdecl;
iter_nextnull51     function  iter_next(iter:PGtkTreeIter):gboolean; cdecl;
iter_childrennull52     function  iter_children({%H-}iter:PGtkTreeIter; {%H-}parent:PGtkTreeIter):gboolean; cdecl;
iter_has_childnull53     function  iter_has_child({%H-}iter:PGtkTreeIter):gboolean; cdecl;
iter_n_childrennull54     function  iter_n_children(iter:PGtkTreeIter):gint; cdecl;
iter_nth_childnull55     function  iter_nth_child(iter:PGtkTreeIter; parent:PGtkTreeIter; n:gint):gboolean; cdecl;
iter_parentnull56     function  iter_parent({%H-}iter:PGtkTreeIter; {%H-}child:PGtkTreeIter):gboolean; cdecl;
57     procedure ref_node({%H-}iter:PGtkTreeIter); cdecl;
58     procedure unref_node({%H-}iter:PGtkTreeIter); cdecl;
59     {%ENDREGION}
60     procedure NotifyRowInserted(AIndex: PtrUInt);
61     procedure NotifyRowDeleted(AIndex: PtrUInt);
TreeModelnull62     function TreeModel: PGtkTreeModel; inline;
63   public
64     ListView: TCustomListView;
65   end;
66 
67   PMyTreeModelObjectClass = ^TLCLTreeModelClass;
68   TLCLTreeModelClass = record
69     parent_class: TGObjectClass;
70   end;
71   TLVItemHack = class(TListItem)
72   end;
73 
74 
LCLListViewModelNewnull75 function LCLListViewModelNew(AListView: TCustomListView): PLCLListViewModel;
LCLLISTVIEW_MODEL_TYPEnull76 function LCLLISTVIEW_MODEL_TYPE: GType;
77 
78 
79 procedure LCLListViewModelClassInit({%H-}g_class:gpointer; {%H-}class_data:gpointer); cdecl;
80 procedure LCLListViewModelInit({%H-}instance:PGTypeInstance; {%H-}g_class:gpointer); cdecl;
81 procedure LCLListViewModelInterfaceInit(g_iface:PGtkTreeModelIface; {%H-}iface_data:gpointer); cdecl;
82 
83 
84 implementation
85 
86 var
87   _LCLLISTVIEW_MODEL_TYPE: gulong = 0;
88 
LCLListViewModelNewnull89 function LCLListViewModelNew(AListView: TCustomListView): PLCLListViewModel;
90 begin
91   Result := g_object_new(LCLLISTVIEW_MODEL_TYPE, nil);
92   Result^.ListView := AListView;
93 end;
94 
LCLLISTVIEW_MODEL_TYPEnull95 function LCLLISTVIEW_MODEL_TYPE: GType;
96 var
97   TypeInfo: TGTypeInfo;
98   INterfaceINfo: TGInterfaceInfo;
99 begin
100   _LCLLISTVIEW_MODEL_TYPE := g_type_from_name(ELCLListViewModel);
101   if _LCLLISTVIEW_MODEL_TYPE = 0 then
102   begin
103     with TypeInfo do
104     begin
105       class_size:= SizeOf(TLCLTreeModelClass);
106       base_init := nil;//TGBaseInitFunc;
107       base_finalize:= nil;// TGBaseFinalizeFunc;
108       class_init := @LCLListViewModelClassInit;// TGClassInitFunc;
109       class_finalize := nil;// TGClassFinalizeFunc;
110       class_data := nil;// gconstpointer;
111       instance_size := SizeOf(TLCLListViewModel);// guint16;
112       n_preallocs := 0;// guint16;
113       instance_init := @LCLListViewModelInit;// TGInstanceInitFunc;
114       value_table := nil;// PGTypeValueTable;
115     end;
116     with InterfaceInfo do
117     begin
118       interface_init := TGInterfaceInitFunc(@LCLListViewModelInterfaceInit);// TGInterfaceInitFunc;
119       interface_finalize := nil; //TGInterfaceFinalizeFunc;
120       interface_data := nil;//gpointer;
121     end;
122     _LCLLISTVIEW_MODEL_TYPE := g_type_register_static(G_TYPE_OBJECT, ELCLListViewModel, @TypeInfo, 0);
123     g_type_add_interface_static(_LCLLISTVIEW_MODEL_TYPE, GTK_TYPE_TREE_MODEL, @InterfaceInfo);
124   end;
125   Result := _LCLLISTVIEW_MODEL_TYPE;
126 end;
127 
128 procedure LCLListViewModelClassInit(g_class:gpointer; class_data:gpointer); cdecl;
129 begin
130 end;
131 
132 procedure LCLListViewModelInit(instance:PGTypeInstance; g_class:gpointer); cdecl;
133 begin
134 end;
135 
136 procedure LCLListViewModelInterfaceInit(g_iface:PGtkTreeModelIface; iface_data:gpointer); cdecl;
137 var
138   P: PPointer;
139 begin
140   // Do not change the order here!!
141   P := @g_iface^.row_changed;
142 
143   P^ := @TLCLListViewModel.row_changed;           Inc(P);
144   P^ := @TLCLListViewModel.row_inserted;          Inc(P);
145   P^ := @TLCLListViewModel.row_has_child_toggled; Inc(P);
146   P^ := @TLCLListViewModel.row_deleted;           Inc(P);
147   P^ := @TLCLListViewModel.rows_reordered;        Inc(P);
148   P^ := @TLCLListViewModel.get_flags;             Inc(P);
149   P^ := @TLCLListViewModel.get_n_columns;         Inc(P);
150   P^ := @TLCLListViewModel.get_column_type;       Inc(P);
151   P^ := @TLCLListViewModel.get_iter;              Inc(P);
152   P^ := @TLCLListViewModel.get_path;              Inc(P);
153   P^ := @TLCLListViewModel.get_value;             Inc(P);
154   P^ := @TLCLListViewModel.iter_next;             Inc(P);
155   P^ := @TLCLListViewModel.iter_children;         Inc(P);
156   P^ := @TLCLListViewModel.iter_has_child;        Inc(P);
157   P^ := @TLCLListViewModel.iter_n_children;       Inc(P);
158   P^ := @TLCLListViewModel.iter_nth_child;        Inc(P);
159   P^ := @TLCLListViewModel.iter_parent;           Inc(P);
160   P^ := @TLCLListViewModel.ref_node;              Inc(P);
161   P^ := @TLCLListViewModel.unref_node;            Inc(P);
162 
163 end;
164 
165 { TLCLListViewModel }
166 
167 
168 procedure TLCLListViewModel.row_changed(path: PGtkTreePath; iter: PGtkTreeIter); cdecl;
169 begin
170 end;
171 
172 procedure TLCLListViewModel.row_inserted(path: PGtkTreePath; iter: PGtkTreeIter); cdecl;
173 begin
174 end;
175 
176 procedure TLCLListViewModel.row_has_child_toggled(path: PGtkTreePath;
177   iter: PGtkTreeIter); cdecl;
178 begin
179 end;
180 
181 procedure TLCLListViewModel.row_deleted(path: PGtkTreePath); cdecl;
182 begin
183 end;
184 
185 procedure TLCLListViewModel.rows_reordered(path: PGtkTreePath;
186   iter: PGtkTreeIter; new_order: Pgint); cdecl;
187 begin
188 end;
189 
get_flagsnull190 function TLCLListViewModel.get_flags(): TGtkTreeModelFlags; cdecl;
191 begin
192   Result := GTK_TREE_MODEL_LIST_ONLY or 0;
193 end;
194 
get_n_columnsnull195 function TLCLListViewModel.get_n_columns(): gint; cdecl;
196 begin
197   Result := 1;
198 end;
199 
get_column_typenull200 function TLCLListViewModel.get_column_type(index: gint): GType; cdecl;
201 begin
202   Result := G_TYPE_POINTER;
203 end;
204 
TLCLListViewModel.get_iternull205 function TLCLListViewModel.get_iter(iter: PGtkTreeIter; path: PGtkTreePath): gboolean; cdecl;
206 var
207   Index: PtrUInt;
208 begin
209   Result := False;
210   Index := gtk_tree_path_get_indices(path)[0];
211   if Index < ListView.Items.Count then
212   begin
213     iter^.user_data:= {%H-}Pointer(Index);
214     Exit(True);
215   end;
216 end;
217 
get_pathnull218 function TLCLListViewModel.get_path(iter: PGtkTreeIter): PGtkTreePath; cdecl;
219 var
220   Index: PtrUint;
221 begin
222   Result := nil;
223   if iter = nil then
224     Exit;
225   Index := {%H-}PtrUint(Iter^.user_data);
226   Result := gtk_tree_path_new_from_indices(Index, -1);
227 end;
228 
229 procedure TLCLListViewModel.get_value(iter: PGtkTreeIter; column: gint;
230   value: PGValue); cdecl;
231 var
232   Index: Integer;
233   Item: TLVItemHack;
234   //ValueType: GType;
235   //SubIndex: Integer;
236 begin
237   Index := {%H-}PtrUint(Iter^.user_data);
238   Item := TLVItemHack(ListView.Items.Item[Index]);
239 
240   g_value_init(value, G_TYPE_POINTER);
241   g_value_set_pointer(value, Pointer(Item));
242 
243 
244 // We use custom renderers so the below is not needed and was never tested :)
245 
246 {
247   Listview Columns in the tree model are stored like so
248   [ 0: Checked. 1: Pixbuf. 2: Caption. 3: Subitem(x) Pixbuf. 4: Subitem(x) Text etc
249 }
250 
251 {  Case column of
252     0:
253        begin
254          g_value_init(value, G_TYPE_BOOLEAN);
255          g_value_set_boolean(value, Item.GetCheckedInternal);
256        end;
257     1:
258        begin
259          g_value_init(value, GTK_TYPE_PIXMAP);
260          //g_value_set_pointer(ListView.im); !!
261          g_value_set_pointer(value, nil);
262        end;
263     2:
264        begin
265          g_value_init(value, G_TYPE_STRING);
266          g_value_set_static_string(value,PChar(Item.Caption));
267        end;
268   else
269     SubIndex := (column - 3) div 2;
270     if Column and 1 = 0 then // Picbuf
271     begin
272       g_value_init(value, GTK_TYPE_PIXMAP);
273       //g_value_set_pointer(ListView.im); !!
274       g_value_set_pointer(value, nil);
275     end
276     else // Text;
277     begin
278       g_value_init(value, G_TYPE_STRING);
279       if SubIndex >= Item.SubItems.Count then
280         g_value_set_static_string(value,PChar(''))
281       else
282         g_value_set_static_string(value,PChar(Item.SubItems.Strings[SubIndex]));
283     end;
284   end;
285 }
286 end;
287 
iter_nextnull288 function TLCLListViewModel.iter_next(iter: PGtkTreeIter): gboolean; cdecl;
289 begin
290   Result := False;
291   if ListView = nil then
292     Exit;
293   Inc({%H-}PtrUInt(Iter^.user_data));
294   Result := {%H-}PtrUint(Iter^.user_data) < ListView.items.Count;
295 end;
296 
TLCLListViewModel.iter_childrennull297 function TLCLListViewModel.iter_children(iter: PGtkTreeIter; parent: PGtkTreeIter): gboolean; cdecl;
298 begin
299   Result := False;
300 end;
301 
iter_has_childnull302 function TLCLListViewModel.iter_has_child(iter: PGtkTreeIter): gboolean; cdecl;
303 begin
304   Result := false;
305 end;
306 
iter_n_childrennull307 function TLCLListViewModel.iter_n_children(iter: PGtkTreeIter): gint; cdecl;
308 begin
309   Result := 0;
310   if (Iter = nil) and (ListView <> nil) then
311     Result := ListView.Items.Count;
312 end;
313 
iter_nth_childnull314 function TLCLListViewModel.iter_nth_child(iter: PGtkTreeIter;
315   parent: PGtkTreeIter; n: gint): gboolean; cdecl;
316 begin
317   Result := False;
318   if (ListView = nil) or (parent <> nil) then
319     Exit;
320   if (Iter <> nil) and (n < ListView.Items.Count) then
321   begin
322     {%H-}PtrUint(Iter^.user_data) := n;
323     Result := True;
324   end;
325 end;
326 
iter_parentnull327 function TLCLListViewModel.iter_parent(iter: PGtkTreeIter; child: PGtkTreeIter): gboolean; cdecl;
328 begin
329   Result := False;
330 end;
331 
332 procedure TLCLListViewModel.ref_node(iter: PGtkTreeIter); cdecl;
333 begin
334 
335 end;
336 
337 procedure TLCLListViewModel.unref_node(iter: PGtkTreeIter); cdecl;
338 begin
339 
340 end;
341 
342 procedure TLCLListViewModel.NotifyRowInserted(AIndex: PtrUInt);
343 var
344   Path: PGtkTreePath;
345   Iter: TGtkTreeIter;
346 begin
347   Iter.user_data := {%H-}Pointer(AIndex);
348   path := gtk_tree_path_new_from_indices(AIndex, -1);
349   //emits a signal
350   gtk_tree_model_row_inserted(TreeModel, path, @iter);
351   gtk_tree_path_free(path);
352 end;
353 
354 procedure TLCLListViewModel.NotifyRowDeleted(AIndex: PtrUInt);
355 var
356   Path: PGtkTreePath;
357 begin
358   path := gtk_tree_path_new_from_indices(AIndex, -1);
359   //emits a signal
360   gtk_tree_model_row_deleted(TreeModel, path);
361   gtk_tree_path_free(path);
362 end;
363 
TLCLListViewModel.TreeModelnull364 function TLCLListViewModel.TreeModel: PGtkTreeModel; inline;
365 begin
366   Result := PGtkTreeModel(@Self);
367 end;
368 
369 end.
370