1{%MainUnit gtk2wscomctrls.pp}
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
11type
12  TLVHack = class(TCustomListView)
13  end;
14  TLVItemHack = class(TListItem)
15  end;
16
17////////////////////////////////////////
18////   Event Code  /////////////////////
19////////////////////////////////////////
20
21function IsOldGtk2: Boolean;
22begin
23  Result := (gtk_major_version = 2) and (gtk_minor_version < 10);
24end;
25
26procedure Gtk2_PixBufFromBitmap(const BitImage:TBitmap; out pixbuf:PGdkPixbuf);
27var
28GDIObj: PGDIObject;
29bitmap:PGdkBitmap;
30Width, Height:gint;
31pixmap: PGdkPixmap;
32begin
33GDIObj := {%H-}PGDIObject(BitImage.Handle);
34  case GDIObj^.GDIBitmapType of
35    gbBitmap:
36      begin
37        bitmap := GDIObj^.GDIBitmapObject;
38        gdk_drawable_get_size(bitmap, @Width, @Height);
39        pixbuf := CreatePixbufFromDrawable(bitmap, nil, False, 0, 0, 0, 0, Width, Height);
40      end;
41    gbPixmap:
42      begin
43        pixmap := GDIObj^.GDIPixmapObject.Image;
44        if pixmap <> nil then
45        begin
46          gdk_drawable_get_size(pixmap, @Width, @Height);
47          bitmap := CreateGdkMaskBitmap(BitImage.Handle, 0);
48          pixbuf := CreatePixbufFromImageAndMask(pixmap, 0, 0, Width, Height, nil, Bitmap);
49        end;
50      end;
51    gbPixbuf:
52      begin
53        pixbuf := gdk_pixbuf_copy(GDIObj^.GDIPixbufObject);
54      end;
55  end;
56end;
57
58procedure Gtk2_ItemCheckedChanged(renderer: PGtkCellRendererToggle; PathStr: Pgchar; WidgetInfo: PWidgetInfo);cdecl;
59var
60  LV: TLVHack;
61  Index: Integer;
62  ListItem: TLVItemHack;
63  ARect: TGdkRectangle;
64  R: TRect;
65  x, y, cellw, cellh: gint;
66begin
67  LV := TLVHack(WidgetInfo^.LCLObject);
68  Index := StrToInt(PathStr);
69  ListItem := TLVItemHack(LV.Items.Item[Index]);
70  if ListItem <> nil then
71  begin
72    ListItem.Checked := not ListItem.GetCheckedInternal;
73    if Assigned(LV.OnItemChecked) then
74      LV.OnItemChecked(TListView(WidgetInfo^.LCLObject), LV.Items.Item[Index]);
75
76    // we must update renderer row, otherwise visually it looks different
77    // if we change toggle state by keyboard (eg. pressing Space key)
78    R := ListItem.DisplayRect(drBounds);
79    ARect := GdkRectFromRect(R);
80    gtk_cell_renderer_get_size(PGtkCellRenderer(renderer),
81      WidgetInfo^.CoreWidget, @ARect, @x,@y, @cellw, @cellh);
82    with R do
83      gtk_widget_queue_draw_area(WidgetInfo^.CoreWidget, Left, Top, cellW, cellH);
84  end;
85end;
86
87procedure Gtk2_ItemFocusChanged(Widget: PGtkWidget; WidgetInfo: PWidgetInfo);cdecl;
88var
89  msg: TLMNotify;
90  NM: TNMListView;
91  path: PGtkTreePath;
92  pstr: PChar;
93  column: PGtkTreeViewColumn;
94  cell: PGtkCellRenderer;
95begin
96  // DebugLn('Gtk2_ItemFocusChanged');
97  // the defocus of the oldrow isn't send
98  if GTK_IS_TREE_VIEW(Widget) then begin
99    path:=nil;
100    column:=nil;
101    gtk_tree_view_get_cursor(PGtkTreeView(Widget), path, column);
102  end
103  else
104  if GTK_IS_ICON_VIEW(Widget) then begin
105    path:=nil;
106    cell:=nil;
107    gtk_icon_view_get_cursor(PGtkIconView(Widget), path, cell);
108  end
109  else
110    path := nil;
111
112  if path = nil then
113    Exit;
114
115  gtk_tree_path_free(path);
116
117  msg.Msg := CN_NOTIFY;
118
119  FillChar(NM{%H-}, SizeOf(NM), 0);
120  NM.hdr.hwndfrom := {%H-}PtrUInt(WidgetInfo^.CoreWidget);
121  NM.hdr.code := LVN_ITEMCHANGED;
122  pstr:=gtk_tree_path_to_string(path);
123  NM.iItem := StrToInt(pstr);
124  g_free(pstr);
125
126  NM.iSubItem := 0;
127  NM.uNewState := LVIS_FOCUSED;
128  NM.uChanged := LVIF_STATE;
129  msg.NMHdr := @NM.hdr;
130  DeliverMessage(WidgetInfo^.LCLObject, msg);
131end;
132
133procedure Gtk2_ItemDeleted({%H-}model: PGtkTreeModel; path: PGtkTreePath; WidgetInfo: PWidgetInfo); cdecl;
134var
135  msg: TLMNotify;
136  NM: TNMListView;
137  pstr:PChar;
138begin
139  //DebugLn('Gtk2_ItemDeleted');
140  msg.Msg := CN_NOTIFY;
141
142  FillChar(NM{%H-}, SizeOf(NM), 0);
143  NM.hdr.hwndfrom := {%H-}PtrUInt(WidgetInfo^.CoreWidget);
144  NM.hdr.code := LVN_DELETEITEM;
145  pstr := gtk_tree_path_to_string(path);
146  NM.iItem := StrToInt(pstr);
147  g_free(pstr);
148  msg.NMHdr := @NM.hdr;
149  DeliverMessage(WidgetInfo^.LCLObject, msg);
150end;
151
152procedure Gtk2_ItemInserted({%H-}model: PGtkTreeModel; path: PGtkTreePAth; {%H-}Iter: PGtkTreeIter; WidgetInfo: PWidgetInfo); cdecl;
153var
154  msg: TLMNotify;
155  NM: TNMListView;
156begin
157  //DebugLn('Gtk2_ItemInserted');
158  msg.Msg := CN_NOTIFY;
159
160  FillChar(NM{%H-}, SizeOf(NM), 0);
161  NM.hdr.hwndfrom := {%H-}PtrUInt(WidgetInfo^.CoreWidget);
162  NM.hdr.code := LVN_INSERTITEM;
163  NM.iItem := gtk_tree_path_get_indices(path)^;
164  msg.NMHdr := @NM.hdr;
165  DeliverMessage(WidgetInfo^.LCLObject, msg);
166end;
167
168//This is only for when the tree view sorts itself. Not needed since the LCL does the sorting.
169//procedure Gtk2_ItemMoved(model: PGtkTreeModel; path: PGtkTreePAth; Iter: PGtkTreeIter; WidgetInfo: PWidgetInfo); cdecl;
170//begin
171//end;
172
173procedure Gtk2_ItemChanged({%H-}model: PGtkTreeModel; {%H-}path: PGtkTreePAth; {%H-}Iter: PGtkTreeIter; {%H-}WidgetInfo: PWidgetInfo); cdecl;
174begin
175 // OnChange Occurs immediately after an item in the list changes.
176 // The Item parameter is the list item that just changed. The Change parameter
177 // indicates the type of change that just occurred. Change is ctText if the
178 // Caption property of the item changed. Change is ctImage if the ImageIndex
179 // property of the item changed or the appropriate image list changed in the
180 // list view. Change is ctState if the Cut, Focused, or Selected property of
181 // the item changed.
182 // DebugLn('Gtk2_ItemChanged');
183end;
184
185
186procedure Gtk2_ColumnClicked(column: PGtkTreeViewColumn; WidgetInfo: PWidgetInfo); cdecl;
187var
188  AColumn: TListColumn;
189  msg: TLMNotify;
190  NM: TNMListView;
191begin
192  AColumn := TListColumn(g_object_get_data(G_OBJECT(column), 'TListColumn'));
193
194  msg.Msg := CN_NOTIFY;
195
196  FillChar(NM{%H-}, SizeOf(NM), 0);
197  NM.hdr.hwndfrom := {%H-}PtrUInt(WidgetInfo^.CoreWidget);
198  NM.hdr.code := LVN_COLUMNCLICK;
199  NM.iItem := -1;
200  NM.iSubItem := AColumn.Index;
201  msg.NMHdr := @NM.hdr;
202  DeliverMessage(WidgetInfo^.LCLObject, msg);
203end;
204
205procedure BroadcastListSelection(Target : Pointer; AHandle : HWND; AIndex : Integer;
206  AState : Boolean);
207var
208  msg: TLMNotify;
209  NM: TNMListView;
210begin
211  msg.Msg := CN_NOTIFY;
212
213  FillChar(NM{%H-}, SizeOf(NM), 0);
214  NM.hdr.hwndfrom := AHandle;
215  NM.hdr.code := LVN_ITEMCHANGED;
216  NM.iItem := AIndex;
217  NM.iSubItem := 0;
218  if AState then
219    NM.uOldState := LVIS_SELECTED
220  else
221    NM.uNewState := LVIS_SELECTED;
222  NM.uChanged := LVIF_STATE;
223  msg.NMHdr := @NM.hdr;
224  if g_object_get_data({%H-}PGObject(TWinControl(Target).Handle),'lcl_gtkwidget_in_update') = nil then
225  begin
226    DeliverMessage(Target, msg);
227    if TLVHack(Target).Selected = TLVHack(Target).Items[AIndex] then
228    begin
229      NM.uOldState := 0;
230      NM.uNewState := LVIS_FOCUSED;
231      NM.uChanged := LVIF_STATE;
232      msg.NMHdr := @NM.hdr;
233      DeliverMessage(Target, msg);
234    end else
235    if TLVHack(Target).Selected = nil then
236    begin
237      NM.uOldState := LVIS_FOCUSED;
238      NM.uNewState := 0;
239      NM.uChanged := LVIF_STATE;
240      msg.NMHdr := @NM.hdr;
241      DeliverMessage(Target, msg);
242    end;
243  end;
244end;
245
246procedure Gtk2_ItemSelectionChanged(selection: PGtkTreeSelection; WidgetInfo: PWidgetInfo); cdecl;
247var
248  Widgets: PTVWidgets;
249  AIndex: String;
250  i: Integer;
251  Indices: Integer;
252  ListIndex: Integer;
253  List: PgList;
254  Path: PGtkTreePath;
255begin
256  // DebugLn('Gtk2_ItemSelectionChanged');
257  Widgets := PTVWidgets(WidgetInfo^.UserData);
258
259  if (widgets = nil) or (Widgets^.ItemCache = nil) or
260    (Widgets^.ItemCache.Count=0) then
261  begin
262    // debugln(' Gtk2_ItemSelectionChanged  ItemCache=nil ',tComponent(widgetInfo^.lclObject).name);
263    if IsOldGtk2 and (Widgets <> nil) and (Widgets^.ItemCache <> nil) then
264    begin
265      // debugLn('ItemsCache is valid ! count ',dbgs(Widgets^.ItemCache.Count));
266      List := gtk_tree_selection_get_selected_rows(Selection, nil);
267      if (List <> nil) then
268      begin
269        if Assigned(Widgets^.OldTreeSelection) then
270        begin
271          // we must iterate because of multiselections
272          for i := 0 to g_list_length(Widgets^.OldTreeSelection) - 1 do
273          begin
274            Path := g_list_nth_data(Widgets^.OldTreeSelection, i);
275            if Path <> nil then
276            begin
277              Indices := gtk_tree_path_get_indices(Path)^;
278              ListIndex := Widgets^.ItemCache.IndexOf(IntToStr(Indices));
279              if ListIndex = -1 then
280                Widgets^.ItemCache.AddObject(IntToStr(Indices), TObject(1))
281              else
282                Widgets^.ItemCache.Objects[ListIndex] := TObject(1);
283            end;
284          end;
285          g_list_free(Widgets^.OldTreeSelection);
286          Widgets^.OldTreeSelection := g_list_alloc;
287          // we must iterate because of multiselections
288          for i := 0 to g_list_length(List) - 1 do
289            g_list_append(Widgets^.OldTreeSelection, g_list_nth_data(List, i));
290        end;
291        // now compare new selection (add or set as selected)
292        for i := 0 to g_list_length(List) - 1 do
293        begin
294          Path := g_list_nth_data(List, i);
295          if Path <> nil then
296          begin
297            Indices := gtk_tree_path_get_indices(Path)^;
298            ListIndex := Widgets^.ItemCache.IndexOf(IntToStr(Indices));
299            if ListIndex = -1 then
300              Widgets^.ItemCache.AddObject(IntToStr(Indices), TObject(0))
301            else
302              Widgets^.ItemCache.Objects[ListIndex] := TObject(0);
303          end;
304        end;
305        g_list_free(List);
306      end else
307      begin
308        // complete selection is clear !
309        if Assigned(Widgets^.OldTreeSelection) then
310        begin
311          for i := 0 to g_list_length(Widgets^.OldTreeSelection) - 1 do
312          begin
313            Path := g_list_nth_data(Widgets^.OldTreeSelection, i);
314            if Path <> nil then
315            begin
316              Indices := gtk_tree_path_get_indices(Path)^;
317              ListIndex := Widgets^.ItemCache.IndexOf(IntToStr(Indices));
318              if ListIndex = -1 then
319                Widgets^.ItemCache.AddObject(IntToStr(Indices), TObject(1))
320              else
321                Widgets^.ItemCache.Objects[ListIndex] := TObject(1);
322            end;
323          end;
324          g_list_free(Widgets^.OldTreeSelection);
325          Widgets^.OldTreeSelection := g_list_alloc;
326        end;
327      end;
328    end else
329      Exit;
330  end;
331
332  // DebugLn('Gtk2_ItemSelectionChanged Trigger OnSelectItem ? ', dbgs(not (wwiInvalidEvent in Widgets^.WidgetInfo^.Flags)));
333  // LCL sent selection !
334  if wwiInvalidEvent in Widgets^.WidgetInfo^.Flags then
335    exit;
336
337  for i := 0 to Widgets^.ItemCache.Count -1 do
338  begin
339    AIndex := Widgets^.ItemCache.Strings[i];
340    BroadcastListSelection(WidgetInfo^.LCLObject, {%H-}PtrUInt(Widgets^.MainView),
341      StrToInt(AIndex), Widgets^.ItemCache.Objects[i] <> nil);
342  end;
343  Widgets^.ItemCache.Clear;
344end;
345
346procedure Gtk2_IconViewSelectionChanged(AIconView: PGtkIconView; WidgetInfo: PWidgetInfo); cdecl;
347var
348  Widgets: PTVWidgets;
349  AIndex: String;
350  i: Integer;
351  List: PGList;
352  Path: PGtkTreePath;
353  pstr: PChar;
354begin
355  Widgets := PTVWidgets(WidgetInfo^.UserData);
356
357  if (Widgets=nil) or (Widgets^.ItemCache=nil) or (Widgets^.ItemCache.Count=0) then
358  begin
359    if (Widgets^.ItemCache <> nil) and (Widgets^.ItemCache.Count = 0) then
360    begin
361      List := gtk_icon_view_get_selected_items(AIconView);
362      if (List <> nil) then
363      begin
364        Path := PGtkTreePath(g_list_first(List)^.data);
365        pstr := gtk_tree_path_to_string(path);
366        Widgets^.ItemCache.Add(pstr);
367        g_free(pstr);
368        g_list_free(List);
369      end else
370        exit;
371    end else
372      Exit;
373  end;
374
375  // LCL already sent selection !
376  if wwiInvalidEvent in Widgets^.WidgetInfo^.Flags then
377    exit;
378
379  for i := 0 to Widgets^.ItemCache.Count -1 do
380  begin
381    AIndex := Widgets^.ItemCache.Strings[i];
382    BroadcastListSelection(WidgetInfo^.LCLObject, {%H-}PtrUInt(Widgets^.MainView),
383      StrToInt(AIndex), Widgets^.ItemCache.Objects[i] <> nil);
384  end;
385  Widgets^.ItemCache.Clear;
386end;
387
388function Gtk2WSLV_ItemSelected({%H-}selection: PGtkTreeSelection; {%H-}model: PGtkTreeModel;
389           path: PGtkTreePath; path_is_currently_selected: GBoolean; WidgetInfo: PWidgetInfo): GBoolean; cdecl;
390var
391  Widgets: PTVWidgets;
392  i: Integer;
393  Item: integer;
394begin
395  // DebugLn('Gtk2_ItemSelected ');
396  // this function is called *before* the item is selected
397  // The result should be True to allow the Item to change selection
398  Result := True;
399
400  Widgets := PTVWidgets(WidgetInfo^.UserData);
401  Item := gtk_tree_path_get_indices(path)^;
402  i := Widgets^.ItemCache.IndexOf(IntToStr(Item));
403  if i = -1 Then
404    Widgets^.ItemCache.AddObject(IntToStr(Item), TObject(PtrInt(Ord(path_is_currently_selected))))
405  else
406    Widgets^.ItemCache.Objects[i] := TObject(PtrInt(Ord(path_is_currently_selected)));
407end;
408
409procedure Gtk2WSLV_ListViewGetCheckedDataFunc({%H-}tree_column: PGtkTreeViewColumn;
410  cell: PGtkCellRenderer; tree_model: PGtkTreeModel; iter: PGtkTreeIter; WidgetInfo: PWidgetInfo); cdecl;
411var
412  APath: PGtkTreePath;
413  ListItem: TLVItemHack;
414begin
415  gtk_tree_model_get(tree_model, iter, [0, @ListItem, -1]);
416
417  if (ListItem = nil) and TCustomListView(WidgetInfo^.LCLObject).OwnerData then
418  begin
419    APath := gtk_tree_model_get_path(tree_model,iter);
420    ListItem := TLVItemHack(TCustomListView(WidgetInfo^.LCLObject).Items[gtk_tree_path_get_indices(APath)^]);
421    gtk_tree_path_free(APath);
422  end;
423
424  if ListItem = nil then
425    Exit;
426  gtk_cell_renderer_toggle_set_active(PGtkCellRendererToggle(cell), ListItem.GetCheckedInternal);
427end;
428
429
430procedure Gtk2WSLV_ListViewGetPixbufDataFuncForColumn(tree_column: PGtkTreeViewColumn;
431  cell: PGtkCellRenderer; tree_model: PGtkTreeModel; iter: PGtkTreeIter; WidgetInfo: PWidgetInfo); cdecl;
432var
433  ListItem: TListItem;
434  Images: TList;
435  Widgets: PTVWidgets;
436  ListColumn: TListColumn;
437  ImageIndex: Integer;
438  ColumnIndex: Integer;
439  APath: PGtkTreePath;
440  ImageList: TCustomImageList;
441  Bmp: TBitmap;
442  pixbuf: PGdkPixbuf;
443begin
444  PGtkCellRendererPixbuf(cell)^.pixbuf := nil;
445  Widgets := PTVWidgets(WidgetInfo^.UserData);
446  gtk_tree_model_get(tree_model, iter, [0, @ListItem, -1]);
447
448  ListColumn := TListColumn(g_object_get_data(G_OBJECT(tree_column), 'TListColumn'));
449  if ListColumn = nil then
450    Exit;
451  ColumnIndex := ListColumn.Index;
452  ImageList := nil;
453  Images := Widgets^.Images;
454  if TCustomListView(WidgetInfo^.LCLObject).OwnerData then
455    ImageList := TLVHack(WidgetInfo^.LCLObject).SmallImages;
456  if (Images = nil) and (ImageList = nil) then
457  begin
458    Exit;
459  end;
460  ImageIndex := -1;
461
462  if (ListItem = nil) and TCustomListView(WidgetInfo^.LCLObject).OwnerData then
463  begin
464    APath := gtk_tree_model_get_path(tree_model,iter);
465    ListItem := TCustomListView(WidgetInfo^.LCLObject).Items[gtk_tree_path_get_indices(APath)^];
466    gtk_tree_path_free(APath);
467  end;
468
469  if ListItem = nil then
470    Exit;
471
472  if ColumnIndex = 0 then
473    ImageIndex := ListItem.ImageIndex
474  else
475    if ColumnIndex -1 <= ListItem.SubItems.Count-1 then
476      ImageIndex := ListItem.SubItemImages[ColumnIndex-1];
477
478  if (ImageList <> nil) and
479    (ImageIndex > -1) and (ImageIndex <= ImageList.Count-1) then
480  begin
481    Bmp := TBitmap.create;
482    try
483      pixbuf := nil;
484      ImageList.GetBitmap(ImageIndex, Bmp);
485      Gtk2_PixBufFromBitmap(Bmp,pixbuf);
486      PGtkCellRendererPixbuf(cell)^.pixbuf :=pixbuf;
487    finally
488      Bmp.Free;
489    end;
490  end else
491  if (ImageIndex > -1) and (ImageIndex <= Images.Count-1) then
492    PGtkCellRendererPixbuf(cell)^.pixbuf := PGdkPixbuf(Images.Items[ImageIndex])
493  else
494    PGtkCellRendererPixbuf(cell)^.pixbuf := nil;
495end;
496
497procedure Gtk2WSLV_ListViewGetPixbufDataFuncForIconView({%H-}cell_layout:PGtkCellLayout;
498  cell: PGtkCellRenderer; tree_model: PGtkTreeModel; iter: PGtkTreeIter; WidgetInfo: PWidgetInfo); cdecl;
499var
500  ListItem: TListItem;
501  Images: TList;
502  Widgets: PTVWidgets;
503  ImageIndex: Integer;
504  APath: PGtkTreePath;
505begin
506  PGtkCellRendererPixbuf(cell)^.pixbuf := nil;
507  Widgets := PTVWidgets(WidgetInfo^.UserData);
508  gtk_tree_model_get(tree_model, iter, [0, @ListItem, -1]);
509
510  Images := Widgets^.Images;
511  if Images = nil then
512    Exit;
513  ImageIndex := -1;
514
515  if (ListItem = nil) and TCustomListView(WidgetInfo^.LCLObject).OwnerData then
516  begin
517    APath := gtk_tree_model_get_path(tree_model,iter);
518    ListItem := TCustomListView(WidgetInfo^.LCLObject).Items[gtk_tree_path_get_indices(APath)^];
519    gtk_tree_path_free(APath);
520  end;
521
522  if ListItem = nil then
523    Exit;
524
525  ImageIndex := ListItem.ImageIndex;
526
527  if (ImageIndex > -1) and (ImageIndex <= Images.Count-1) then
528    PGtkCellRendererPixbuf(cell)^.pixbuf := PGdkPixbuf(Images.Items[ImageIndex])
529  else
530    PGtkCellRendererPixbuf(cell)^.pixbuf := nil;
531end;
532
533{ TGtk2WSCustomListView }
534
535class procedure TGtk2WSCustomListView.SetPropertyInternal(const ALV: TCustomListView;
536  const Widgets: PTVWidgets; const AProp: TListViewProperty;
537  const AIsSet: Boolean);
538const
539  BoolToSelectionMode: array[Boolean] of TGtkSelectionMode = (
540    GTK_SELECTION_SINGLE,
541    GTK_SELECTION_MULTIPLE
542  );
543begin
544  with Widgets^ do begin
545    case AProp of
546      lvpAutoArrange: begin
547        // TODO: implement ??
548      end;
549      lvpCheckboxes:
550        begin
551          if TLVHack(ALV).ViewStyle in [vsReport,vsList] then
552            AddRemoveCheckboxRenderer(ALV, GetWidgetInfo(Widgets^.MainView), AIsSet);
553        end;
554      lvpColumnClick: begin
555        // allow only column modifications when in report mode
556        if TLVHack(ALV).ViewStyle <> vsReport then Exit;
557        gtk_tree_view_set_headers_clickable(PGtkTreeView(MainView), AIsSet);
558      end;
559      lvpFlatScrollBars: begin
560        // TODO: implement ??
561      end;
562      lvpFullDrag: begin
563        // TODO: implement ??
564      end;
565      lvpGridLines: begin
566        // TODO: better implementation
567        // maybe possible with some cellwidget hacking
568        // this create rows with alternating colors
569        if GTK_IS_TREE_VIEW(MainView) then
570        begin
571          if gtk_tree_view_set_grid_lines <> nil then
572          begin
573            if AIsSet then
574              gtk_tree_view_set_grid_lines(PGtkTreeView(MainView), GTK_TREE_VIEW_GRID_LINES_BOTH)
575            else
576              gtk_tree_view_set_grid_lines(PGtkTreeView(MainView), GTK_TREE_VIEW_GRID_LINES_NONE);
577
578          end else
579            gtk_tree_view_set_rules_hint(PGtkTreeView(MainView), AIsSet);
580        end;
581      end;
582      lvpHideSelection: begin
583        // TODO: implement
584        // should be possible with some focus in/out events
585      end;
586      lvpHotTrack: begin
587        // TODO: implement
588        // should be possible with some mouse tracking
589      end;
590      lvpMultiSelect: begin
591        if GTK_IS_TREE_VIEW(MainView) then
592          gtk_tree_selection_set_mode(TreeSelection, BoolToSelectionMode[AIsSet])
593        else
594        if GTK_IS_ICON_VIEW(MainView) then
595          gtk_icon_view_set_selection_mode(PGtkIconView(MainView), BoolToSelectionMode[AIsSet]);
596      end;
597      lvpOwnerDraw:
598      begin
599        // It must send CN_DRAWITEM with ItemID and proper rect of item
600        // then LCL does all other stuff. Note that OwnerDraw should work only
601        // in case of vsReport, according to embarcadero docs.
602        // http://docwiki.embarcadero.com/Libraries/XE4/en/Vcl.ComCtrls.TCustomListView.OnDrawItem
603        // NOTE: this is automatically handled by cell renderer (Gtk2CellRenderer).
604      end;
605      lvpReadOnly: begin
606        // TODO: implement inline editor ?
607      end;
608      lvpRowSelect: begin
609        // TODO: implement ???
610        // how to do cell select
611      end;
612      lvpShowColumnHeaders: begin
613        // allow only column modifications when in report mode
614        if TLVHack(ALV).ViewStyle <> vsReport then Exit;
615        gtk_tree_view_set_headers_visible(GTK_TREE_VIEW (MainView), AIsSet);
616      end;
617      lvpShowWorkAreas: begin
618        // TODO: implement ???
619      end;
620      lvpWrapText: begin
621        // TODO: implement ???
622      end;
623    end;
624  end;
625end;
626
627class procedure TGtk2WSCustomListView.SetNeedDefaultColumn(const ALV: TCustomListView; const AValue: Boolean);
628var
629  Widgets: PTVWidgets;
630  WidgetInfo: PWidgetInfo;
631  GtkColumn: PGtkTreeViewColumn;
632  pixrenderer,
633  textrenderer: PGtkCellRenderer;
634begin
635  if not WSCheckHandleAllocated(ALV, 'SetNeedDefaultColumn')
636  then Exit;
637
638  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
639  WidgetInfo := GetWidgetInfo({%H-}PGtkWidget(ALV.Handle));
640
641  GtkColumn := g_object_get_data(G_OBJECT(Widgets^.MainView), 'LCL_DEFAULT_COLUMN');
642  if AValue then
643  begin
644    if GtkColumn = nil then
645    begin
646      GtkColumn := gtk_tree_view_column_new();
647
648      gtk_widget_unset_flags(PGtkWidget(GtkColumn), GTK_CAN_FOCUS);
649
650      // add renderers
651      pixrenderer := gtk_cell_renderer_pixbuf_new();
652      textrenderer := LCLIntfCellRenderer_New;
653
654      if GTK_IS_TREE_VIEW(Widgets^.MainView) then
655      begin
656        gtk_tree_view_column_pack_start(GtkColumn, pixrenderer, FALSE);
657        //gtk_tree_view_column_set_attributes(GtkColumn, pixrenderer,['pixbuf', 0, nil]);
658        gtk_tree_view_column_pack_start(GtkColumn, textrenderer, True);
659        //gtk_tree_view_column_set_attributes(GtkColumn, textrenderer, ['text',0, nil]);
660
661        gtk_tree_view_column_set_cell_data_func(GtkColumn, pixrenderer,  TGtkTreeCellDataFunc(@Gtk2WSLV_ListViewGetPixbufDataFuncForColumn), WidgetInfo, nil);
662        gtk_tree_view_column_set_cell_data_func(GtkColumn, textrenderer, TGtkTreeCellDataFunc(@LCLIntfCellRenderer_CellDataFunc), WidgetInfo, nil);
663
664        // insert column
665        gtk_tree_view_insert_column(GTK_TREE_VIEW(Widgets^.MainView), GtkColumn, 0);
666      end
667      else
668      if GTK_IS_ICON_VIEW(Widgets^.MainView) then
669      begin
670        gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(Widgets^.MainView), pixrenderer, False);
671        gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(Widgets^.MainView), textrenderer, True);
672        gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(Widgets^.MainView), pixrenderer, TGtkCellLayoutDataFunc(@Gtk2WSLV_ListViewGetPixbufDataFuncForIconView), WidgetInfo, nil);
673        gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(Widgets^.MainView), textrenderer, TGtkCellLayoutDataFunc(@LCLIntfCellRenderer_CellDataFunc), WidgetInfo, nil);
674      end;
675      g_object_set_data(G_OBJECT(Widgets^.MainView), 'LCL_DEFAULT_COLUMN', GtkColumn);
676    end;
677  end
678  else begin // No Column Needed
679    if GtkColumn <> nil then
680    begin
681      if GTK_IS_TREE_VIEW(Widgets^.MainView) and GTK_IS_TREE_VIEW_COLUMN(GtkColumn) then
682        gtk_tree_view_remove_column(PGtkTreeView(Widgets^.MainView), GtkColumn)
683      else
684      if GTK_IS_TREE_VIEW_COLUMN(GtkColumn) and G_IS_OBJECT(GtkColumn) then
685        g_object_unref(GtkColumn);
686      g_object_set_data(G_OBJECT(Widgets^.MainView), 'LCL_DEFAULT_COLUMN', nil);
687    end;
688  end;
689end;
690
691class procedure TGtk2WSCustomListView.AddRemoveCheckboxRenderer(
692  const ALV: TCustomListView; const WidgetInfo: PWidgetInfo; const Add: Boolean);
693var
694  togglerenderer,
695  pixrenderer,
696  textrenderer: PGtkCellRenderer;
697  column: PGtkTreeViewColumn;
698  renderers: PGList;
699begin
700  column := gtk_tree_view_get_column(PGtkTreeView(WidgetInfo^.CoreWidget), 0);
701
702  if column = nil then
703    Exit;
704
705  renderers := gtk_tree_view_column_get_cell_renderers(column);
706  textrenderer := PGtkCellRenderer(g_list_last(renderers)^.data);
707  pixrenderer := PGtkCellRenderer(g_list_last(renderers)^.prev^.data);
708  g_list_free(renderers);
709  g_object_ref(G_OBJECT(pixrenderer));
710  g_object_ref(G_OBJECT(textrenderer));
711
712  if Add then
713  begin
714    gtk_cell_layout_clear(GTK_CELL_LAYOUT(column));
715    togglerenderer := gtk_cell_renderer_toggle_new();
716
717    gtk_tree_view_column_pack_start(column, togglerenderer, FALSE);
718    gtk_tree_view_column_pack_start(column, pixrenderer, FALSE);
719    gtk_tree_view_column_pack_start(column, textrenderer, True);
720    gtk_tree_view_column_set_cell_data_func(column, togglerenderer, TGtkTreeCellDataFunc(@Gtk2WSLV_ListViewGetCheckedDataFunc), WidgetInfo, nil);
721    gtk_tree_view_column_set_cell_data_func(column, pixrenderer,  TGtkTreeCellDataFunc(@Gtk2WSLV_ListViewGetPixbufDataFuncForColumn), WidgetInfo, nil);
722    gtk_tree_view_column_set_cell_data_func(column, textrenderer, TGtkTreeCellDataFunc(@LCLIntfCellRenderer_CellDataFunc), WidgetInfo, nil);
723    // connect toggled signal
724    g_signal_connect(togglerenderer, 'toggled', TGTKSignalFunc(@Gtk2_ItemCheckedChanged), GetWidgetInfo({%H-}PGtkWidget(ALV.Handle)));
725
726  end
727  else
728  begin
729    gtk_cell_layout_clear(GTK_CELL_LAYOUT(column));
730
731    gtk_tree_view_column_pack_start(column, pixrenderer, FALSE);
732    gtk_tree_view_column_pack_start(column, textrenderer, True);
733    gtk_tree_view_column_set_cell_data_func(column, pixrenderer,  TGtkTreeCellDataFunc(@Gtk2WSLV_ListViewGetPixbufDataFuncForColumn), WidgetInfo, nil);
734    gtk_tree_view_column_set_cell_data_func(column, textrenderer, TGtkTreeCellDataFunc(@LCLIntfCellRenderer_CellDataFunc), WidgetInfo, nil);
735
736  end;
737  if G_IS_OBJECT(pixrenderer) then
738    g_object_unref(G_OBJECT(pixrenderer));
739  if G_IS_OBJECT(textrenderer) then
740    g_object_unref(G_OBJECT(textrenderer));
741end;
742
743class function TGtk2WSCustomListView.GetViewModel(const AView: PGtkWidget): PGtkTreeModel;
744begin
745  if GTK_IS_TREE_VIEW(AView) then
746    Result := gtk_tree_view_get_model(PGtkTreeView(AView))
747  else
748  if GTK_IS_ICON_VIEW(AView) then
749    Result := gtk_icon_view_get_model(PGtkIconView(AView))
750  else
751    Result := nil;
752end;
753
754class procedure TGtk2WSCustomListView.SetListCallbacks(const AScrollWidget: PGtkWidget;
755  const Widgets: PTVWidgets; const AWidgetInfo: PWidgetInfo);
756begin
757  TGtk2WSBaseScrollingWinControl.SetCallbacks(AScrollWidget, AWidgetInfo);
758  TGtk2WSWinControl.SetCallbacks(PGtkObject(Widgets^.MainView), TComponent(AWidgetInfo^.LCLObject));
759
760  // the callbacks for OnColumnClick are set when the column is created in ColumnInsert
761
762  if GTK_IS_TREE_VIEW(Widgets^.MainView) then
763  begin
764    if IsOldGtk2 then
765      // bug in 2.8, issue #19820
766    else
767      gtk_tree_selection_set_select_function(Widgets^.TreeSelection,TGtkTreeSelectionFunc(@Gtk2WSLV_ItemSelected), gpointer(AWidgetInfo),nil);
768    SignalConnect(PGtkWidget(Widgets^.TreeSelection), 'changed',           @Gtk2_ItemSelectionChanged,    AWidgetInfo);
769    SignalConnect(PGtkWidget(Widgets^.MainView),      'toggle-cursor-row', @Gtk2_ItemFocusChanged,        AWidgetInfo);
770  end
771  else
772  if GTK_IS_ICON_VIEW(Widgets^.MainView) then
773  begin
774    SignalConnect(Widgets^.MainView, 'selection-changed',  @Gtk2_IconViewSelectionChanged,    AWidgetInfo);
775    SignalConnect(Widgets^.MainView, 'toggle-cursor-item', @Gtk2_ItemFocusChanged,        AWidgetInfo);
776  end;
777  SignalConnect(PGtkWidget(Widgets^.TreeModel), 'row-changed',  @Gtk2_ItemChanged,  AWidgetInfo);
778  SignalConnect(PGtkWidget(Widgets^.TreeModel), 'row-inserted', @Gtk2_ItemInserted, AWidgetInfo);
779  SignalConnect(PGtkWidget(Widgets^.TreeModel), 'row-deleted',  @Gtk2_ItemDeleted,  AWidgetInfo);
780end;
781
782class procedure TGtk2WSCustomListView.ColumnDelete(const ALV: TCustomListView;
783  const AIndex: Integer);
784var
785  Widgets: PTVWidgets;
786  GtkColumn: PGtkTreeViewColumn;
787begin
788  if not WSCheckHandleAllocated(ALV, 'ColumnDelete')
789  then Exit;
790
791  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
792
793  if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
794    Exit;
795
796  GtkColumn := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), AIndex);
797  if (GtkColumn<>nil) and GTK_IS_TREE_VIEW_COLUMN(GtkColumn) then
798    gtk_tree_view_remove_column(PGtkTreeView(Widgets^.MainView), GtkColumn);
799end;
800
801class function TGtk2WSCustomListView.ColumnGetWidth(const ALV: TCustomListView;
802  const AIndex: Integer; const AColumn: TListColumn): Integer;
803var
804  Widgets: PTVWidgets;
805  GtkColumn: PGtkTreeViewColumn;
806begin
807  Result := -1;
808
809  if not WSCheckHandleAllocated(ALV, 'ColumnGetWidth')
810  then Exit;
811
812  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
813  if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
814    Exit;
815  GtkColumn := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), AIndex);
816  if GtkColumn <> nil then
817  begin
818    Result := gtk_tree_view_column_get_width(GtkColumn);
819    if Result = 0 then
820      Result := gtk_tree_view_column_get_fixed_width(GtkColumn);
821  end;
822end;
823
824class procedure TGtk2WSCustomListView.ColumnInsert(const ALV: TCustomListView;
825  const AIndex: Integer; const AColumn: TListColumn);
826var
827  Widgets: PTVWidgets;
828  column: PGtkTreeViewColumn;
829  pixrenderer,
830  textrenderer: PGtkCellRenderer;
831  WidgetInfo: PWidgetInfo;
832begin
833  if not WSCheckHandleAllocated(ALV, 'ColumnInsert')
834  then Exit;
835
836  WidgetInfo := GetWidgetInfo({%H-}PGtkWidget(ALV.Handle));
837  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
838
839  if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
840    Exit;
841
842  column := gtk_tree_view_column_new();
843
844  gtk_widget_unset_flags(PGtkWidget(column), GTK_CAN_FOCUS);
845
846  // add renderers
847  pixrenderer := gtk_cell_renderer_pixbuf_new();
848  textrenderer := LCLIntfCellRenderer_New;//gtk_cell_renderer_text_new();
849
850  gtk_tree_view_column_pack_start(column, pixrenderer, False);
851  //gtk_tree_view_column_set_attributes(column, pixrenderer,['pixbuf', RealIndex, nil]);
852  gtk_tree_view_column_pack_start(column, textrenderer, True);
853  //gtk_tree_view_column_set_attributes(column, textrenderer, ['text', 0, nil]);
854
855  gtk_tree_view_column_set_cell_data_func(column, pixrenderer,  TGtkTreeCellDataFunc(@Gtk2WSLV_ListViewGetPixbufDataFuncForColumn), WidgetInfo, nil);
856  gtk_tree_view_column_set_cell_data_func(column, textrenderer, TGtkTreeCellDataFunc(@LCLIntfCellRenderer_CellDataFunc), WidgetInfo, nil);
857  //gtk_tree_view_column_set_cell_data_func(column, textrenderer, TGtkTreeCellDataFunc(@Gtk2WSLV_ListViewGetTextDataFunc), WidgetInfo, nil);
858
859
860  //store the TColumn in the column data for callbacks
861  g_object_set_data(G_OBJECT(column), PChar('TListColumn'), gpointer(AColumn));
862
863  // set callback for OnClick
864  SignalConnect(PGtkWidget(column), 'clicked', @Gtk2_ColumnClicked, Widgets^.WidgetInfo);
865
866  // insert column
867  gtk_tree_view_insert_column(GTK_TREE_VIEW(Widgets^.MainView), Column, AIndex);
868
869  //set clickable
870  gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
871// do not set these here, it will be set by the lcl
872(*
873  // set title
874  gtk_tree_view_column_set_title(column, PChar(AColumn.Caption));
875  //set width
876  gtk_tree_view_column_set_fixed_width(Column, AColumn.Width);
877  // set Visible
878  gtk_tree_view_column_set_visible(Column, AColumn.Visible);
879  // set MinWidth
880  if AColumn.MinWidth > 0 then
881    gtk_tree_view_column_set_min_width(Column, AColumn.MinWidth);
882  // set MaxWidth
883  if AColumn.MaxWidth > 0 then
884    gtk_tree_view_column_set_max_width(Column, AColumn.MaxWidth);
885
886  //set resizable
887  gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN (column), True);
888
889*)
890end;
891
892class procedure TGtk2WSCustomListView.ColumnMove(const ALV: TCustomListView;
893  const AOldIndex, ANewIndex: Integer; const AColumn: TListColumn);
894var
895  Widgets: PTVWidgets;
896  Column: PGtkTreeViewColumn;
897  PrevColumn: PGtkTreeViewColumn;
898begin
899  if not WSCheckHandleAllocated(ALV, 'ColumnMove') then Exit;
900
901  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
902  if not GTK_IS_TREE_VIEW(Widgets^.MainView) then Exit;
903
904  Column := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), AOldIndex);
905  if Column <> nil then
906  begin
907    if ANewIndex = 0 then
908      PrevColumn := nil
909    else
910      PrevColumn := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), ANewIndex);
911    gtk_tree_view_move_column_after(PGtkTreeView(Widgets^.MainView), Column, PrevColumn);
912  end;
913end;
914
915class procedure TGtk2WSCustomListView.ColumnSetAlignment(const ALV: TCustomListView;
916  const AIndex: Integer; const AColumn: TListColumn;
917  const AAlignment: TAlignment);
918var
919  Widgets: PTVWidgets;
920  GtkColumn: PGtkTreeViewColumn;
921  Alignment: gfloat;
922  Value: TGValue;
923  renderers: PGList;
924  textrenderer: PGtkCellRenderer;
925begin
926  if not WSCheckHandleAllocated(ALV, 'ColumnSetAlignment')
927  then Exit;
928
929  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
930
931  if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
932    Exit;
933
934  GtkColumn := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), AIndex);
935  if GtkColumn <> nil then
936  begin
937    renderers := gtk_tree_view_column_get_cell_renderers(GtkColumn);
938    textrenderer := PGtkCellRenderer(g_list_last(renderers)^.data);
939    g_list_free(renderers);
940
941    Alignment := AlignToGtkAlign(AAlignment);
942    Value.g_type := G_TYPE_FLOAT;
943    Value.data[0].v_float:= Alignment;
944    g_object_set_property(G_OBJECT(textrenderer), PChar('xalign'), @Value);
945
946    {now we call set alignment because it calls update over visible rows in col}
947    gtk_tree_view_column_set_alignment(GtkColumn, Alignment);
948  end;
949end;
950
951class procedure TGtk2WSCustomListView.ColumnSetAutoSize(const ALV: TCustomListView;
952  const AIndex: Integer; const AColumn: TListColumn; const AAutoSize: Boolean);
953const
954  SizingMap: array[Boolean] of TGtkTreeViewColumnSizing = (
955    GTK_TREE_VIEW_COLUMN_FIXED,
956    GTK_TREE_VIEW_COLUMN_AUTOSIZE
957  );
958var
959  Widgets: PTVWidgets;
960  GtkColumn: PGtkTreeViewColumn;
961begin
962  if not WSCheckHandleAllocated(ALV, 'ColumnSetAutoSize')
963  then Exit;
964
965  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
966
967  if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
968    Exit;
969
970  GtkColumn := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), AIndex);
971  if GtkColumn <> nil then
972  begin
973    gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(GtkColumn), True);
974    gtk_tree_view_column_set_sizing(GtkColumn, SizingMap[AAutoSize]);
975  end;
976end;
977
978class procedure TGtk2WSCustomListView.ColumnSetCaption(const ALV: TCustomListView;
979  const AIndex: Integer; const AColumn: TListColumn; const ACaption: String);
980var
981  Widgets: PTVWidgets;
982  GtkColumn: PGtkTreeViewColumn;
983begin
984  if not WSCheckHandleAllocated(ALV, 'ColumnSetCaption')
985  then Exit;
986
987  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
988
989  if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
990    Exit;
991
992  GtkColumn := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), AIndex);
993  if GtkColumn <> nil then
994    gtk_tree_view_column_set_title(GtkColumn, PChar(ACaption));
995end;
996
997class procedure TGtk2WSCustomListView.ColumnSetImage(const ALV: TCustomListView;
998  const AIndex: Integer; const AColumn: TListColumn; const AImageIndex: Integer
999  );
1000begin
1001  if not WSCheckHandleAllocated(ALV, 'ColumnSetImage')
1002  then Exit;
1003
1004  // ToDo: TGtk2WSCustomListView.ColumnSetImage
1005  //DebugLn('TODO: Gtk2. TGtk2WSCustomListView.ColumnSetImage');
1006end;
1007
1008class procedure TGtk2WSCustomListView.ColumnSetMaxWidth(const ALV: TCustomListView;
1009  const AIndex: Integer; const AColumn: TListColumn; const AMaxWidth: Integer);
1010var
1011  Widgets: PTVWidgets;
1012  GtkColumn: PGtkTreeViewColumn;
1013begin
1014  if not WSCheckHandleAllocated(ALV, 'ColumnSetMaxWidth')
1015  then Exit;
1016
1017  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
1018  if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
1019    Exit;
1020  GtkColumn := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), AIndex);
1021  if GtkColumn <> nil then
1022    gtk_tree_view_column_set_max_width(GtkColumn, AMaxWidth - Ord(AMaxWidth=0));
1023end;
1024
1025class procedure TGtk2WSCustomListView.ColumnSetMinWidth(const ALV: TCustomListView;
1026  const AIndex: Integer; const AColumn: TListColumn; const AMinWidth: integer);
1027var
1028  Widgets: PTVWidgets;
1029  GtkColumn: PGtkTreeViewColumn;
1030begin
1031  if not WSCheckHandleAllocated(ALV, 'ColumnSetMinWidth')
1032  then Exit;
1033
1034  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
1035  if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
1036    Exit;
1037  GtkColumn := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), AIndex);
1038  if GtkColumn <> nil then
1039    gtk_tree_view_column_set_min_width(GtkColumn, AMinWidth - Ord(AMinWidth=0));
1040end;
1041
1042class procedure TGtk2WSCustomListView.ColumnSetWidth(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AWidth: Integer);
1043var
1044  Widgets: PTVWidgets;
1045  GtkColumn: PGtkTreeViewColumn;
1046begin
1047  if not WSCheckHandleAllocated(ALV, 'ColumnSetWidth')
1048  then Exit;
1049
1050  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
1051  if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
1052    Exit;
1053  GtkColumn := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), AIndex);
1054  if GtkColumn <> nil then
1055  begin
1056    GtkColumn^.width := 0;
1057    gtk_tree_view_column_set_fixed_width(GtkColumn, AWidth + Ord(AWidth<1));
1058  end;
1059end;
1060
1061class procedure TGtk2WSCustomListView.ColumnSetVisible(const ALV: TCustomListView;
1062  const AIndex: Integer; const AColumn: TListColumn; const AVisible: Boolean);
1063var
1064  Widgets: PTVWidgets;
1065  GtkColumn: PGtkTreeViewColumn;
1066begin
1067  if not WSCheckHandleAllocated(ALV, 'ColumnSetVisible')
1068  then Exit;
1069
1070  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
1071  if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
1072    Exit;
1073  with Widgets^ do
1074  begin
1075    GtkColumn := gtk_tree_view_get_column(PGtkTreeView(MainView), AIndex);
1076    if AVisible then
1077      g_object_set_data(G_OBJECT(GtkColumn), PChar('Visible'), gpointer(ptrint(1)))
1078    else
1079      g_object_set_data(G_OBJECT(GtkColumn), PChar('Visible'), gpointer(ptrint(0)));
1080    if TLVHack(ALV).ViewStyle = vsReport then begin
1081      gtk_tree_view_column_set_visible(GtkColumn, AVisible);
1082    end;
1083  end;
1084end;
1085
1086class procedure TGtk2WSCustomListView.ColumnSetSortIndicator(
1087  const ALV: TCustomListView; const AIndex: Integer;
1088  const AColumn: TListColumn; const ASortIndicator: TSortIndicator);
1089const
1090  GtkOrder : array [ TSortIndicator] of TGtkSortType = (0, GTK_SORT_ASCENDING, GTK_SORT_DESCENDING);
1091var
1092  Widgets: PTVWidgets;
1093  GtkColumn: PGtkTreeViewColumn;
1094begin
1095  if not WSCheckHandleAllocated(ALV, 'ColumnSetCaption')
1096  then Exit;
1097
1098  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
1099
1100  if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
1101    Exit;
1102
1103  GtkColumn := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), AIndex);
1104  if GtkColumn <> nil then
1105  begin
1106    if ASortIndicator = siNone then
1107      gtk_tree_view_column_set_sort_indicator(GtkColumn, false)
1108    else
1109    begin
1110      gtk_tree_view_column_set_sort_indicator(GtkColumn, true);
1111      gtk_tree_view_column_set_sort_order(GtkColumn, GtkOrder[ASortIndicator]);
1112    end;
1113  end;
1114end;
1115
1116class procedure TGtk2WSCustomListView.ItemDelete(const ALV: TCustomListView;
1117  const AIndex: Integer);
1118var
1119  Widgets: PTVWidgets;
1120  {$IFDEF USEORIGTREEMODEL}
1121  Iter: TGtkTreeIter;
1122  {$ENDIF}
1123begin
1124  if not WSCheckHandleAllocated(ALV, 'ItemDelete')
1125  then Exit;
1126
1127  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
1128
1129  with Widgets^ do begin
1130    {$IFDEF USEORIGTREEMODEL}
1131    if gtk_tree_model_iter_nth_child(TreeModel, @Iter, nil, AIndex) then
1132    begin
1133      gtk_list_store_remove(TreeModel, @Iter);
1134    end;
1135    {$ELSE}
1136    PLCLListViewModel(TreeModel)^.NotifyRowDeleted(AIndex);
1137    {$ENDIF}
1138  end;
1139end;
1140
1141class function TGtk2WSCustomListView.ItemDisplayRect(const ALV: TCustomListView; const AIndex, ASubItem: Integer;
1142  ACode: TDisplayCode): TRect;
1143var
1144  Widgets: PTVWidgets;
1145  ItemRect, IconRect: TGdkRectangle;
1146  Column: PGtkTreeViewColumn;
1147  Path: PGtkTreePath;
1148  X, Y, L, T, W, H: GInt;
1149  ARect: TGdkRectangle;
1150  R: TRect;
1151  ANewCell: PGtkCellRenderer;
1152  ANewPath: PGtkTreePath;
1153  APGList: PGList;
1154  pixrenderer: PGtkCellRenderer;
1155  AWidth: gint;
1156  AHeight: gint;
1157begin
1158  Result := Rect(0, 0, 0, 0);
1159  if not WSCheckHandleAllocated(ALV, 'ItemDisplayRect') then
1160    Exit;
1161
1162  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
1163  with Widgets^ do
1164  begin
1165    if gtk_widget_realized(MainView) = False then
1166      exit;
1167    Path := gtk_tree_path_new_from_indices(AIndex, -1);
1168    try
1169      if GTK_IS_TREE_VIEW(MainView) then
1170      begin
1171        Column := gtk_tree_view_get_column(PGtkTreeView(MainView), ASubItem);
1172        gtk_tree_view_get_cell_area(PGtkTreeView(MainView), Path, Column, @ItemRect);
1173        if gtk_tree_view_get_headers_visible(PGtkTreeView(MainView)) then
1174        begin
1175          gtk_tree_view_column_cell_get_size(gtk_tree_view_get_column(PGtkTreeView(MainView), 0),
1176            @ARect, @L, @T, @W, @H);
1177          inc(ItemRect.y, H);
1178        end;
1179
1180        if (ACode in [drIcon, drLabel]) and not TLVHack(ALV).OwnerDraw then
1181        begin
1182          IconRect := ItemRect;
1183          APGList := gtk_tree_view_column_get_cell_renderers(gtk_tree_view_get_column(PGtkTreeView(MainView), 0));
1184          pixrenderer := PGtkCellRenderer(g_list_last(APGList)^.prev^.data);
1185          gtk_cell_renderer_get_fixed_size(pixrenderer, @AWidth, @AHeight);
1186          if AWidth > 0 then
1187            IconRect.Width := AWidth - 2;
1188          if AHeight > 0 then
1189            IconRect.Height := AHeight - 2;
1190          g_list_free(APGList);
1191          if ACode = drIcon then
1192            ItemRect := IconRect
1193          else
1194          begin
1195            ItemRect.x += AWidth + 2;
1196            ItemRect.y += 2; // offset
1197            ItemRect.Width -= AWidth + 2;
1198            ItemRect.Height -= 2; // offset
1199          end;
1200        end;
1201
1202      end
1203      else
1204      if GTK_IS_ICON_VIEW(MainView) then
1205      begin
1206        ItemRect.x := 0;
1207        ItemRect.y := 0;
1208        ItemRect.width := gtk_icon_view_get_item_width(PGtkIconView(MainView));
1209        ItemRect.height := 0;
1210        if Path <> nil then
1211        begin
1212          ANewPath := nil;
1213          ANewCell := nil;
1214          R := ALV.ClientRect;
1215          l := 0;
1216          t := 0;
1217          Result := Rect(0, 0, 0, 0);
1218          while t < R.Bottom - 1 do
1219          begin
1220            l := 0;
1221            while l < R.Right - 1 do
1222            begin
1223              if gtk_icon_view_get_item_at_pos(PGtkIconView(MainView), l, t, ANewPath, ANewCell) then
1224              begin
1225                if (ANewPath <> nil) and (gtk_tree_path_compare(Path, ANewPath) = 0) then
1226                begin
1227                  gtk_cell_renderer_get_size(ANewCell, PGtkWidget(MainView), @ItemRect, @x,@y,@w,@h);
1228                  Result := Rect(l, t, w + l, h + t);
1229                  ItemRect := GdkRectFromRect(Result);
1230                  if ANewPath <> nil then
1231                    gtk_tree_path_free(ANewPath);
1232                  break;
1233                end;
1234                if ANewPath <> nil then
1235                  gtk_tree_path_free(ANewPath);
1236              end;
1237              inc(l, 1);
1238            end;
1239
1240            inc(t, 1);
1241
1242            if not IsRectEmpty(Result) then
1243              break;
1244          end;
1245        end;
1246      end;
1247    finally
1248      gtk_tree_path_free(Path);
1249    end;
1250    Result := RectFromGdkRect(ItemRect);
1251  end;
1252end;
1253
1254class procedure TGtk2WSCustomListView.ItemExchange(const ALV: TCustomListView;
1255  AItem: TListItem; const AIndex1, AIndex2: Integer);
1256var
1257  Widgets: PTVWidgets;
1258  Path: PGtkTreePath;
1259  ItemRect: TGdkRectangle;
1260begin
1261  if not WSCheckHandleAllocated(ALV, 'ItemExchange') then
1262    exit;
1263  // gtk2 needs only update
1264  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
1265  if GetViewModel(Widgets^.MainView) = nil then
1266    exit;
1267
1268  if not gtk_widget_realized(Widgets^.MainView) then
1269    exit;
1270
1271  if GTK_IS_TREE_VIEW(Widgets^.MainView) then
1272  begin
1273    Path := gtk_tree_path_new_from_indices(AIndex1, -1);
1274    gtk_tree_view_get_cell_area(PGtkTreeView(Widgets^.MainView), Path, nil, @ItemRect);
1275    gtk_tree_path_free(Path);
1276    if ItemRect.height = 0 then
1277    begin
1278      Path := gtk_tree_path_new_from_indices(AIndex2, -1);
1279      gtk_tree_view_get_cell_area(PGtkTreeView(Widgets^.MainView), Path, nil, @ItemRect);
1280      gtk_tree_path_free(Path);
1281    end;
1282  end else
1283    ItemRect.height := 1; // force redraw
1284
1285  if ItemRect.height <> 0 then // item is visible
1286    gtk_widget_queue_draw(Widgets^.MainView);
1287end;
1288
1289class procedure TGtk2WSCustomListView.ItemMove(const ALV: TCustomListView;
1290  AItem: TListItem; const AFromIndex, AToIndex: Integer);
1291var
1292  Widgets: PTVWidgets;
1293  Path: PGtkTreePath;
1294  ItemRect: TGdkRectangle;
1295begin
1296  if not WSCheckHandleAllocated(ALV, 'ItemMove') then
1297    exit;
1298  // gtk2 needs only update
1299  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
1300  if GetViewModel(Widgets^.MainView) = nil then
1301    exit;
1302
1303  if not gtk_widget_realized(Widgets^.MainView) then
1304    exit;
1305
1306  if GTK_IS_TREE_VIEW(Widgets^.MainView) then
1307  begin
1308    Path := gtk_tree_path_new_from_indices(AFromIndex, -1);
1309    gtk_tree_view_get_cell_area(PGtkTreeView(Widgets^.MainView), Path, nil, @ItemRect);
1310    gtk_tree_path_free(Path);
1311    if ItemRect.height = 0 then
1312    begin
1313      Path := gtk_tree_path_new_from_indices(AToIndex, -1);
1314      gtk_tree_view_get_cell_area(PGtkTreeView(Widgets^.MainView), Path, nil, @ItemRect);
1315      gtk_tree_path_free(Path);
1316    end;
1317  end else
1318    ItemRect.height := 1; // force redraw
1319
1320  if ItemRect.height <> 0 then // item is visible
1321    gtk_widget_queue_draw(Widgets^.MainView);
1322end;
1323
1324class function TGtk2WSCustomListView.ItemGetChecked(const ALV: TCustomListView;
1325  const AIndex: Integer; const AItem: TListItem): Boolean;
1326begin
1327  Result := TLVItemHack(AItem).GetCheckedInternal;
1328end;
1329
1330class function TGtk2WSCustomListView.ItemGetState(const ALV: TCustomListView;
1331  const AIndex: Integer; const AItem: TListItem; const AState: TListItemState;
1332  out AIsSet: Boolean): Boolean;
1333var
1334  Widgets: PTVWidgets;
1335  Path: PGtkTreePath;
1336  pstr: PChar;
1337  Column: PGtkTreeViewColumn;
1338  Cell: PGtkCellRenderer;
1339begin
1340  Result := False;
1341
1342  if not WSCheckHandleAllocated(ALV, 'ItemGetState')
1343  then Exit;
1344
1345  AIsSet := False;
1346
1347  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
1348
1349  with Widgets^ do
1350  begin
1351    if GetViewModel(MainView) = nil then
1352      Exit; // we are in the midst of a begin update end update pair and the following will fail and cause gtk debug messages
1353    case AState of
1354      lisCut,
1355      lisDropTarget:
1356      begin
1357        //TODO: do something with the rowcolor ?
1358      end;
1359
1360      lisFocused:
1361      begin
1362        if GTK_IS_TREE_VIEW(MainView) then begin
1363          Path:=nil;
1364          Column:=nil;
1365          gtk_tree_view_get_cursor(PGtkTreeView(MainView), Path, Column)
1366        end
1367        else
1368        if GTK_IS_ICON_VIEW(MainView) then
1369          gtk_icon_view_get_cursor(PGtkIconView(MainView), Path, Cell{%H-})
1370        else
1371          Path := nil;
1372        if (Path<>nil) then
1373        begin
1374          pstr := gtk_tree_path_to_string(path);
1375          AIsSet:=(StrToInt(pstr) = AIndex);
1376          g_free(pstr);
1377        end else
1378          AIsSet:=false;
1379
1380        gtk_tree_path_free(Path);
1381        Result := True;
1382      end;
1383
1384      lisSelected:
1385      begin
1386        Path := gtk_tree_path_new_from_string(PChar(IntToStr(AIndex)));
1387        if GTK_IS_TREE_VIEW(MainView) then
1388          AIsSet := gtk_tree_selection_path_is_selected(TreeSelection, Path)
1389        else
1390        if GTK_IS_ICON_VIEW(MainView) then
1391          AIsSet := gtk_icon_view_path_is_selected(PGtkIconView(MainView), Path)
1392        else
1393          AIsSet := False;
1394        gtk_tree_path_free(Path);
1395        Result := True;
1396      end;
1397    end;
1398  end;
1399end;
1400
1401class procedure TGtk2WSCustomListView.ItemInsert(const ALV: TCustomListView;
1402  const AIndex: Integer; const AItem: TListItem);
1403var
1404  Widgets: PTVWidgets;
1405  {$IFDEF USEORIGTREEMODEL}
1406  Iter: TGtkTreeIter;
1407  Index: Integer;
1408  {$ENDIF}
1409begin
1410  if not WSCheckHandleAllocated(ALV, 'ItemInsert')
1411  then Exit;
1412
1413
1414  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
1415
1416  with Widgets^ do
1417  begin
1418    {$IFDEF USEORIGTREEMODEL}
1419    if AIndex = -1 then
1420      Index := gtk_tree_model_iter_n_children(TreeModel, nil)
1421    else
1422      Index := AIndex;
1423    gtk_list_store_insert_with_values(PGtkListStore(TreeModel), @Iter, Index, 0, Pointer(AItem), -1);
1424    {$ELSE}
1425    if not (lisfWSItemsCreated in ALV.Items.Flags) then
1426      Exit;
1427
1428    PLCLListViewModel(TreeModel)^.NotifyRowInserted(AIndex);
1429    {$ENDIF}
1430  end;
1431end;
1432
1433class procedure TGtk2WSCustomListView.ItemSetChecked(
1434  const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem;
1435  const AChecked: Boolean);
1436begin
1437  if not WSCheckHandleAllocated(ALV, 'ItemSetChecked')
1438  then Exit;
1439  // nothing needed here
1440end;
1441
1442class procedure TGtk2WSCustomListView.ItemSetImage(const ALV: TCustomListView;
1443  const AIndex: Integer; const AItem: TListItem; const ASubIndex,
1444  AImageIndex: Integer);
1445var
1446  Widgets: PTVWidgets;
1447  Path: PGtkTreePath;
1448  ItemRect: TGdkRectangle;
1449  BitImage: TBitmap;
1450  pixbuf: PGDKPixBuf;
1451  i, ImgListWidth: Integer;
1452  ImgList: TCustomImageList;
1453  ImgListRes: TCustomImageListResolution;
1454
1455begin
1456  if not WSCheckHandleAllocated(ALV, 'ItemSetImage')
1457  then Exit;
1458  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
1459  with Widgets^ do
1460  begin
1461    if not gtk_widget_realized(MainView) then
1462    begin
1463      // DebugLn('WARNING: TGtk2WSCustomListView.ItemSetImage: MainView is not realized.');
1464      Exit;
1465    end;
1466    Path := gtk_tree_path_new_from_indices(AIndex, -1);
1467    if GTK_IS_TREE_VIEW(MainView) then
1468      gtk_tree_view_get_cell_area(PGtkTreeView(MainView), Path, nil, @ItemRect)
1469    else
1470      ItemRect.height := 1; // force redraw
1471    gtk_tree_path_free(Path);
1472    if ItemRect.height <> 0 then // item is visible
1473    begin
1474      if (TListView(ALV).ViewStyle in [vsSmallIcon, vsReport, vsList]) then
1475      begin
1476        ImgList := TListView(ALV).SmallImages;
1477        ImgListWidth := TListView(ALV).SmallImagesWidth;
1478      end
1479      else
1480      if (TListView(ALV).ViewStyle = vsIcon) then
1481      begin
1482        ImgList := TListView(ALV).LargeImages;
1483        ImgListWidth := TListView(ALV).LargeImagesWidth;
1484      end;
1485
1486      if Assigned(ImgList) and (ImgList.Count > 0) and (AImageIndex >= 0) then
1487      begin
1488        ImgListRes := ImgList.ResolutionForPPI[ImgListWidth, ALV.Font.PixelsPerInch, ALV.GetCanvasScaleFactor].Resolution;
1489        if (ImgList.Count <> Widgets^.Images.Count) then
1490        begin
1491          if (TListView(ALV).ViewStyle in [vsSmallIcon, vsReport, vsList]) then
1492            SetImageList(ALV, lvilSmall, ImgListRes)
1493          else
1494            SetImageList(ALV, lvilLarge, ImgListRes);
1495          exit;
1496        end;
1497
1498        if (Widgets^.Images <> nil) then
1499        begin
1500          for i := 0 to Widgets^.Images.Count-1 do
1501            if i = AImageIndex then
1502              gdk_pixbuf_unref(PGdkPixBuf(Widgets^.Images.Items[i]));
1503
1504          pixbuf := nil;
1505          BitImage := TBitmap.Create;
1506          try
1507            ImgListRes.GetBitmap(AImageIndex, BitImage);
1508            Gtk2_PixBufFromBitmap(BitImage,pixbuf);
1509            Widgets^.Images.Items[AImageIndex] := pixbuf;
1510            if GTK_IS_TREE_VIEW(MainView) then
1511              gtk_tree_view_column_queue_resize(gtk_tree_view_get_column(PGtkTreeView(MainView), ASubIndex));
1512          finally
1513            BitImage.Free;
1514          end;
1515        end;
1516      end;
1517      gtk_widget_queue_draw(MainView);
1518    end;
1519  end;
1520end;
1521
1522class procedure TGtk2WSCustomListView.ItemSetState(const ALV: TCustomListView;
1523  const AIndex: Integer; const AItem: TListItem; const AState: TListItemState;
1524  const AIsSet: Boolean);
1525var
1526  Widgets: PTVWidgets;
1527  Path: PGtkTreePath;
1528  BroadcastMsg: Boolean;
1529begin
1530  if not WSCheckHandleAllocated(ALV, 'ItemSetState')
1531  then Exit;
1532
1533  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
1534  // wwiInvalidEvent flag save us from infinite loop !
1535  // when this flag is included TreeSelection 'changed' won't
1536  // trigger - and it shouldn't LCL setted up selection.
1537  // fixes #16399
1538  Include(Widgets^.WidgetInfo^.Flags, wwiInvalidEvent);
1539  try
1540    BroadcastMsg := False;
1541    with Widgets^ do
1542    begin
1543      if GetViewModel(MainView) = nil then
1544        Exit; // we are in the midst of a begin update end update pair and the following will fail and cause gtk debug messages
1545      case AState of
1546        lisCut,
1547        lisDropTarget:
1548        begin
1549          //TODO: do something with the rowcolor ?
1550        end;
1551
1552        lisFocused:
1553        begin
1554          //gtk2 iter has no focus??
1555          Path := gtk_tree_path_new_from_string(PChar(IntToStr(AIndex)));
1556          if GTK_IS_TREE_VIEW(MainView) then
1557            gtk_tree_view_set_cursor(PGtkTreeView(MainView), Path, nil, False)
1558          else
1559          if GTK_IS_ICON_VIEW(MainView) then
1560            gtk_icon_view_set_cursor(PGtkIconView(MainView), Path, nil, False);
1561          gtk_tree_path_free(Path);
1562        end;
1563
1564        lisSelected:
1565        begin
1566          Path := gtk_tree_path_new_from_string(PChar(IntToStr(AIndex)));
1567          if GTK_IS_TREE_VIEW(MainView) then
1568          begin
1569            if AIsSet and not gtk_tree_selection_path_is_selected(TreeSelection, Path) then
1570            begin
1571              gtk_tree_selection_select_path(TreeSelection, Path);
1572              BroadcastMsg := True;
1573            end else
1574            if not AIsSet and gtk_tree_selection_path_is_selected(TreeSelection, Path) then
1575            begin
1576              gtk_tree_selection_unselect_path(TreeSelection, Path);
1577              BroadcastMsg := True;
1578            end;
1579          end
1580          else
1581          if GTK_IS_ICON_VIEW(MainView) then
1582          begin
1583            if AIsSet and not gtk_icon_view_path_is_selected(PGtkIconView(MainView), Path) then
1584            begin
1585              gtk_icon_view_select_path(PGtkIconView(MainView), Path);
1586              BroadCastMsg := True;
1587            end else
1588            if not AIsSet and gtk_icon_view_path_is_selected(PGtkIconView(MainView), Path) then
1589            begin
1590              gtk_icon_view_unselect_path(PGtkIconView(MainView), Path);
1591              BroadCastMsg := True;
1592            end;
1593          end;
1594          gtk_tree_path_free(Path);
1595          if BroadcastMsg then
1596            BroadCastListSelection(ALV, {%H-}PtrUInt(MainView), AIndex, not AIsSet);
1597        end;
1598      end;
1599    end;
1600  finally
1601    Exclude(Widgets^.WidgetInfo^.Flags, wwiInvalidEvent);
1602  end;
1603end;
1604
1605class procedure TGtk2WSCustomListView.ItemSetText(const ALV: TCustomListView;
1606  const AIndex: Integer; const AItem: TListItem; const ASubIndex: Integer;
1607  const AText: String);
1608var
1609  Widgets: PTVWidgets;
1610  Path: PGtkTreePath;
1611  ItemRect: TGdkRectangle;
1612begin
1613  // ToDo: TGtk2WSCustomListView.ItemSetText: this function queues a draw. Is this correct?
1614
1615  if not WSCheckHandleAllocated(ALV, 'ItemSetText')
1616  then Exit;
1617
1618  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
1619  with Widgets^ do
1620  begin
1621    if not gtk_widget_realized(MainView) then
1622      Exit;
1623
1624    if GTK_IS_TREE_VIEW(MainView) then
1625    begin
1626      Path := gtk_tree_path_new_from_indices(AIndex, -1);
1627      gtk_tree_view_get_cell_area(PGtkTreeView(MainView), Path, nil, @ItemRect);
1628      gtk_tree_path_free(Path);
1629    end
1630    else
1631      ItemRect.height := 1; // force redraw
1632
1633    if ItemRect.height <> 0 then // item is visible
1634      gtk_widget_queue_draw(MainView);
1635  end;
1636end;
1637
1638class procedure TGtk2WSCustomListView.ItemShow(const ALV: TCustomListView;
1639  const AIndex: Integer; const AItem: TListItem; const PartialOK: Boolean);
1640var
1641  Widgets: PTVWidgets;
1642  Path: PGtkTreePath;
1643begin
1644  if not WSCheckHandleAllocated(ALV, 'ItemShow')
1645  then Exit;
1646
1647  // TODO: TGtk2WSCustomListView.ItemShow check for partial visiblity. currently scrolls to the Item to make it fully visible
1648  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
1649
1650  with Widgets^ do
1651  begin
1652    Path := gtk_tree_path_new_from_indices(AIndex, -1);
1653    if GTK_IS_TREE_VIEW(MainView) then
1654      gtk_tree_view_scroll_to_cell(PGtkTreeView(MainView), Path, nil, False, 0, 0)
1655    else
1656    if GTK_IS_ICON_VIEW(MainView) then
1657      gtk_icon_view_scroll_to_path(PGtkIconView(MainView), Path, False, 0, 0);
1658    gtk_tree_path_free(Path);
1659  end;
1660end;
1661
1662class function TGtk2WSCustomListView.ItemGetPosition(const ALV: TCustomListView; const AIndex: Integer): TPoint;
1663var
1664  Widgets: PTVWidgets;
1665  Path: PGtkTreePath;
1666  ARect: TGdkRectangle;
1667  Column: PGtkTreeViewColumn;
1668begin
1669  if not WSCheckHandleAllocated(ALV, 'ItemGetPosition')
1670  then Exit;
1671
1672  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
1673
1674  Path := gtk_tree_path_new_from_indices(AIndex, -1);
1675  with Widgets^ do
1676  begin
1677    if GTK_IS_TREE_VIEW(MainView) then
1678    begin
1679      Column := gtk_tree_view_get_column(PGtkTreeView(MainView), 0);
1680      gtk_tree_view_get_cell_area(PGtkTreeView(MainView), Path, Column, @ARect);
1681      Result.X := ARect.x;
1682      Result.Y := Arect.y;
1683    end
1684    else
1685    if GTK_IS_ICON_VIEW(MainView) then
1686    begin
1687      // todo: gtk gives no way to get item rectangle, while internally it uses it
1688      Result.X := 0;
1689      Result.Y := 0;
1690    end;
1691  end;
1692  gtk_tree_path_free(Path);
1693end;
1694
1695class procedure TGtk2WSCustomListView.ItemUpdate(const ALV: TCustomListView;
1696  const AIndex: Integer; const AItem: TListItem);
1697{$IFDEF USEORIGTREEMODEL}
1698var
1699  Widgets: PTVWidgets;
1700  Iter: TGtkTreeIter;
1701  {$ENDIF}
1702begin
1703  if not WSCheckHandleAllocated(ALV, 'ItemUpdate')
1704  then Exit;
1705  {$IFDEF USEORIGTREEMODEL}
1706  GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets);
1707
1708  with Widgets^ do
1709    if gtk_tree_model_iter_nth_child(TreeModel, @Iter, nil, AIndex) then
1710      gtk_list_store_set(PGtkListStore(TreeModel), @Iter, [0, Pointer(AItem), -1]);
1711  {$ENDIF}
1712end;
1713
1714class function TGtk2WSCustomListView.CreateHandle(const AWinControl: TWinControl;
1715  const AParams: TCreateParams): HWND;
1716var
1717  Widgets: PTVWidgets;
1718  OrigScrollingData: PBaseScrollingWinControlData;
1719  //ListViewData: PCustomListViewData;
1720  //Allocation: TGTKAllocation;
1721  ScrollWidget: PGtkScrolledWindow;
1722  {$IFDEF USEORIGTREEMODEL}
1723  PtrType: GType;
1724  {$ENDIF}
1725  SS: TPoint;
1726begin
1727  Result := TGtk2WSBaseScrollingWinControl.CreateHandle(AWinControl, AParams);
1728  if Result = 0 then Exit;
1729
1730  ScrollWidget := {%H-}PGtkScrolledWindow(Result);
1731
1732  gtk_widget_unset_flags(ScrollWidget^.hscrollbar, GTK_CAN_FOCUS);
1733  gtk_widget_unset_flags(ScrollWidget^.vscrollbar, GTK_CAN_FOCUS);
1734
1735  SS := Gtk2TranslateScrollStyle(TListView(AWinControl).ScrollBars);
1736  gtk_scrolled_window_set_policy(ScrollWidget,SS.X, SS.Y);
1737
1738  gtk_scrolled_window_set_shadow_type(ScrollWidget,
1739    BorderStyleShadowMap[TCustomListView(AWinControl).BorderStyle]);
1740
1741  gtk_widget_show(PGtkWidget(ScrollWidget));
1742
1743  Widgets := nil;
1744
1745  New(Widgets);
1746  with Widgets^ do
1747  begin
1748    ItemCache := TStringList.Create;
1749    Images := nil;
1750    OldTreeSelection := nil;
1751
1752    {$IFDEF USEORIGTREEMODEL}
1753    PtrType := G_TYPE_POINTER;
1754    TreeModel := gtk_list_store_newv(1, @PtrType);
1755    {$ELSE}
1756    TreeModel:= LCLListViewModelNew(TCustomListView(AWinControl));
1757    {$ENDIF}
1758
1759    if TLVHack(AWinControl).ViewStyle in [vsIcon,vsSmallIcon] then
1760    begin
1761      MainView := gtk_icon_view_new_with_model(TreeModel);
1762      TreeSelection := nil;
1763      if TLVHack(AWinControl).IconOptions.Arrangement = iaTop then
1764        gtk_icon_view_set_columns(PGtkIconView(MainView), -1)
1765      else
1766        gtk_icon_view_set_columns(PGtkIconView(MainView), 1);
1767    end
1768    else
1769    begin
1770      if IsOldGtk2 then
1771        OldTreeSelection := g_list_alloc;
1772      MainView := gtk_tree_view_new_with_model(TreeModel);
1773      TreeSelection := PGtkTreeSelection(gtk_tree_view_get_selection(PGtkTreeView(MainView)));
1774    end;
1775    g_object_unref(G_OBJECT(TreeModel));
1776
1777    // we added +1 because Ord(vsIcon) returns 0, so it's nil ptr
1778    g_object_set_data(PGObject(MainView),'lcllistviewstyle', {%H-}gpointer(PtrInt(Ord(TLVHack(AWinControl).ViewStyle) + 1)));
1779
1780    gtk_container_add(GTK_CONTAINER(ScrollWidget),PGtkWidget(MainView));
1781
1782    // create widget info
1783    // already created in TGtkWSBaseScrollingWinControl
1784    // Replace the ScrollingInfo with our info
1785    WidgetInfo := GetWidgetInfo(ScrollWidget);
1786    OrigScrollingData := WidgetInfo^.UserData;
1787    Widgets^.ScrollingData := OrigScrollingData^;
1788
1789    WidgetInfo^.UserData := Widgets;
1790
1791    Dispose(OrigScrollingData);
1792    WidgetInfo^.CoreWidget := PGtkWidget(MainView);
1793    g_object_set_data(Pointer(MainView), 'widgetinfo', WidgetInfo);
1794    gtk_widget_show_all(PGtkWidget(MainView));
1795    if not AWinControl.HandleObjectShouldBeVisible and not (csDesigning in AWinControl.ComponentState) then
1796      gtk_widget_hide(PGtkWidget(ScrollWidget));
1797    SetListCallbacks(PGtkWidget(ScrollWidget), Widgets, Widgets^.WidgetInfo);
1798  end;
1799end;
1800
1801class procedure TGtk2WSCustomListView.DestroyHandle(const AWinControl: TWinControl);
1802var
1803  Widgets: PTVWidgets;
1804  i: Integer;
1805begin
1806  GetCommonTreeViewWidgets({%H-}PGtkWidget(AWinControl.Handle), Widgets);
1807  // on widget destroy we have no ItemDeleted notification and we must destroy ItemCache ourself
1808  // if things will change please remove this destroy
1809  if Widgets^.ItemCache <> nil then
1810    Widgets^.ItemCache.Free;
1811  Widgets^.ItemCache := nil;
1812  if Widgets^.OldTreeSelection <> nil then
1813  begin
1814    g_list_free(Widgets^.OldTreeSelection);
1815    Widgets^.OldTreeSelection := nil;
1816  end;
1817  if Widgets^.Images <> nil then
1818  begin
1819    for i := 0 to Widgets^.Images.Count-1 do
1820      if Widgets^.Images.Items[i] <> nil then
1821        gdk_pixbuf_unref(PGDKPixBuf(Widgets^.Images.Items[i]));
1822    FreeAndNil(Widgets^.Images);
1823  end;
1824  TWSWinControlClass(ClassParent).DestroyHandle(AWinControl);
1825end;
1826
1827class procedure TGtk2WSCustomListView.BeginUpdate(const ALV: TCustomListView);
1828begin
1829  if not WSCheckHandleAllocated(ALV, 'BeginUpdate') then
1830    exit;
1831  g_object_set_data({%H-}PGObject(ALV.Handle),'lcl_gtkwidget_in_update', ALV);
1832end;
1833
1834class procedure TGtk2WSCustomListView.EndUpdate(const ALV: TCustomListView);
1835begin
1836  if not WSCheckHandleAllocated(ALV, 'EndUpdate') then
1837    exit;
1838  g_object_set_data({%H-}PGObject(ALV.Handle),'lcl_gtkwidget_in_update', nil);
1839end;
1840
1841class function TGtk2WSCustomListView.GetBoundingRect(const ALV: TCustomListView): TRect;
1842begin
1843  Result:=Rect(0,0,0,0);
1844
1845  if not WSCheckHandleAllocated(ALV, 'GetBoundingRect')
1846  then Exit;
1847
1848  //DebugLn('TODO: TGtk2WSCustomListView.GetBoundingRect');
1849end;
1850
1851class function TGtk2WSCustomListView.GetDropTarget(const ALV: TCustomListView): Integer;
1852var
1853  Widgets: PTVWidgets;
1854begin
1855  // TODO: implement
1856  Result := -1;
1857
1858  if not WSCheckHandleAllocated(ALV, 'GetDropTarget')
1859  then Exit;
1860
1861  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
1862end;
1863
1864class function TGtk2WSCustomListView.GetFocused(const ALV: TCustomListView): Integer;
1865var
1866  Widgets: PTVWidgets;
1867  Path: PGtkTreePath;
1868  Column: PGtkTreeViewColumn;
1869  Cell: PGtkCellRenderer;
1870begin
1871  Result := -1;
1872
1873  if not WSCheckHandleAllocated(ALV, 'GetFocused')
1874  then Exit;
1875
1876  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
1877
1878  with Widgets^ do
1879  begin
1880    if GTK_IS_TREE_VIEW(MainView) then begin
1881      Path:=nil;
1882      Column:=nil;
1883      gtk_tree_view_get_cursor(PGtkTreeView(MainView), Path, Column);
1884    end
1885    else begin
1886      if GTK_IS_ICON_VIEW(MainView) then begin
1887        Cell:=nil;
1888        gtk_icon_view_get_cursor(PGtkIconView(MainView), Path, Cell);
1889      end
1890      else
1891        Path := nil;
1892    end;
1893    if Path <> nil then
1894    begin
1895      Result := StrToInt(PChar(Path));
1896      gtk_tree_path_free(Path);
1897    end;
1898  end;
1899end;
1900
1901class function TGtk2WSCustomListView.GetHoverTime(const ALV: TCustomListView): Integer;
1902var
1903  Widgets: PTVWidgets;
1904begin
1905  // TODO: implement
1906  Result := -1; // = default
1907
1908  if not WSCheckHandleAllocated(ALV, 'GetHoverTime')
1909  then Exit;
1910
1911  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
1912  with Widgets^ do
1913  begin
1914
1915  end;
1916end;
1917
1918class function TGtk2WSCustomListView.GetItemAt(const ALV: TCustomListView; x, y: integer): Integer;
1919var
1920  Widgets: PTVWidgets;
1921  ItemPath: PGtkTreePath;
1922  Column: PGtkTreeViewColumn;
1923  cx, cy: gint;
1924begin
1925  Result := -1;
1926  if not WSCheckHandleAllocated(ALV, 'GetItemAt')
1927  then Exit;
1928
1929  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
1930
1931  if GTK_IS_TREE_VIEW(Widgets^.MainView) then
1932  begin
1933    // gtk2 >= 2.19 changed treeview api
1934    if gtk_minor_version >= 19 then
1935    begin
1936      gdk_window_get_position(gtk_tree_view_get_bin_window(PGtkTreeView(Widgets^.MainView)), @cx, @cy);
1937      Dec(x, cx);
1938      Dec(y, cy);
1939    end else
1940    begin
1941      // convert X, Y to bin window coords
1942      x := x + Round(PGtkTreeView(Widgets^.MainView)^.priv^.hadjustment^.value);
1943      if GTK_TREE_VIEW_FLAG_SET(PGtkTreeView(Widgets^.MainView), GTK_TREE_VIEW_HEADERS_VISIBLE) then
1944      begin
1945        gdk_window_get_size(PGtkTreeView(Widgets^.MainView)^.priv^.header_window, @cx, @cy);
1946        y := y - cy;
1947      end;
1948    end;
1949    ItemPath:=nil;
1950    Column:=nil;
1951    if gtk_tree_view_get_path_at_pos(PGtkTreeView(Widgets^.MainView), x, y, ItemPath, Column, nil, nil) then
1952    begin
1953      if ItemPath <> nil then
1954      begin
1955        Result := gtk_tree_path_get_indices(ItemPath)^;
1956        gtk_tree_path_free(ItemPath);
1957      end;
1958    end;
1959  end
1960  else
1961  if GTK_IS_ICON_VIEW(Widgets^.MainView) then
1962  begin
1963    ItemPath := gtk_icon_view_get_path_at_pos(PGtkIconView(Widgets^.MainView),
1964      x + Widgets^.ScrollingData.HValue, y + Widgets^.ScrollingData.VValue);
1965    if ItemPath <> nil then
1966    begin
1967      Result := gtk_tree_path_get_indices(ItemPath)^;
1968      gtk_tree_path_free(ItemPath);
1969    end;
1970  end;
1971end;
1972
1973class function TGtk2WSCustomListView.GetSelCount(const ALV: TCustomListView): Integer;
1974var
1975  Widgets: PTVWidgets;
1976  AList: PGList;
1977begin
1978  Result := 0;
1979
1980  if not WSCheckHandleAllocated(ALV, 'GetSelCount')
1981  then Exit;
1982
1983  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
1984  with Widgets^ do
1985  begin
1986    if GTK_IS_TREE_VIEW(MainView) then
1987      AList := gtk_tree_selection_get_selected_rows(TreeSelection, nil)
1988    else
1989    if GTK_IS_ICON_VIEW(MainView) then
1990      AList := gtk_icon_view_get_selected_items(PGtkIconView(MainView))
1991    else
1992      Exit;
1993    if AList <> nil then
1994    begin
1995      Result := g_list_length(AList);
1996      g_list_free(AList);
1997    end;
1998  end;
1999end;
2000
2001class function TGtk2WSCustomListView.GetSelection(const ALV: TCustomListView): Integer;
2002var
2003  Widgets: PTVWidgets;
2004  Iter: TGtkTreeIter;
2005  Path: PGtkTreePath;
2006  AList: PGList;
2007begin
2008  Result := -1;
2009
2010  if not WSCheckHandleAllocated(ALV, 'GetSelection')
2011  then Exit;
2012
2013  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
2014
2015  with Widgets^ do
2016  begin
2017    if GTK_IS_TREE_VIEW(MainView) then
2018      gtk_tree_selection_get_selected(TreeSelection, nil, @Iter)
2019    else
2020    if GTK_IS_ICON_VIEW(MainView) then
2021    begin
2022      AList := gtk_icon_view_get_selected_items(PGtkIconView(MainView));
2023      if AList <> nil then
2024      begin
2025        Path := g_list_first(AList)^.data;
2026        g_list_free(AList);
2027      end else
2028        Path := nil;
2029    end;
2030    Path := gtk_tree_model_get_path(TreeModel, @Iter);
2031    Result := StrToInt(PChar(Path));
2032    gtk_tree_path_free(Path);
2033  end;
2034end;
2035
2036class function TGtk2WSCustomListView.GetTopItem(const ALV: TCustomListView): Integer;
2037var
2038  Res: Boolean;
2039  s, e: PGtkTreePath;
2040  Widgets: PTVWidgets;
2041  Num: Pgint;
2042begin
2043  Result := -1;
2044  if not WSCheckHandleAllocated(ALV, 'GetTopItem') then
2045    exit;
2046  Res := false;
2047  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
2048  with Widgets^ do
2049  begin
2050    if GTK_IS_TREE_VIEW(MainView) then
2051      Res := gtk_tree_view_get_visible_range(PGtkTreeView(Widgets^.MainView), s, e)
2052    else
2053      if GTK_IS_ICON_VIEW(MainView) then
2054        Res := gtk_icon_view_get_visible_range(PGtkTreeView(Widgets^.MainView), s, e)
2055      else
2056        Exit;
2057  end;
2058  if Res then
2059  begin
2060    Num := gtk_tree_path_get_indices(s);
2061    if Num <> nil then Result := Num^;
2062    gtk_tree_path_free(s);
2063    gtk_tree_path_free(e);
2064  end;
2065end;
2066
2067class function TGtk2WSCustomListView.GetViewOrigin(const ALV: TCustomListView): TPoint;
2068var
2069  Widgets: PTVWidgets;
2070begin
2071  if not WSCheckHandleAllocated(ALV, 'GetViewOrigin')
2072  then begin
2073    Result := Point(0, 0);
2074    Exit;
2075  end;
2076
2077  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
2078
2079  // not really needed to retrieve the adjustments, since the TVWidgets has this info also based on events
2080  Result := Point(Widgets^.ScrollingData.HValue, Widgets^.ScrollingData.VValue);
2081end;
2082
2083class function TGtk2WSCustomListView.GetVisibleRowCount(const ALV: TCustomListView): Integer;
2084var
2085  Res: Boolean;
2086  s, e: PGtkTreePath;
2087  Widgets: PTVWidgets;
2088  Num1,Num2: Pgint;
2089begin
2090  Result := -1;
2091
2092  if not WSCheckHandleAllocated(ALV, 'GetVisibleRowCount') then
2093    exit;
2094
2095   Result := 0;
2096   Res := false;
2097   GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
2098   with Widgets^ do
2099   begin
2100     if GTK_IS_TREE_VIEW(MainView) then
2101       Res := gtk_tree_view_get_visible_range(PGtkTreeView(Widgets^.MainView), s, e)
2102     else
2103       if GTK_IS_ICON_VIEW(MainView) then
2104         Res := gtk_icon_view_get_visible_range(PGtkTreeView(Widgets^.MainView), s, e)
2105       else
2106         Exit;
2107   end;
2108   if Res then
2109   begin
2110     Num1 := gtk_tree_path_get_indices(s);
2111     Num2 := gtk_tree_path_get_indices(e);
2112     if (Num1 <> nil) and (Num2 <> nil) then Result := Num2^-Num1^+1;
2113     gtk_tree_path_free(s);
2114     gtk_tree_path_free(e);
2115   end;
2116end;
2117
2118class procedure TGtk2WSCustomListView.SelectAll(const ALV: TCustomListView;
2119  const AIsSet: Boolean);
2120var
2121  Widgets: PTVWidgets;
2122begin
2123  if not WSCheckHandleAllocated(ALV, 'SelectAll') then
2124    exit;
2125  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
2126  Include(Widgets^.WidgetInfo^.Flags, wwiInvalidEvent);
2127  try
2128    with Widgets^ do
2129    begin
2130      if GTK_IS_TREE_VIEW(MainView) then
2131      begin
2132        if AIsSet then
2133          gtk_tree_selection_select_all(gtk_tree_view_get_selection(PGtkTreeView(MainView)))
2134        else
2135          gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(PGtkTreeView(MainView)));
2136      end else
2137      begin
2138        if GTK_IS_ICON_VIEW(MainView) then
2139        begin
2140          if AIsSet then
2141            gtk_icon_view_select_all(PGtkIconView(MainView))
2142          else
2143            gtk_icon_view_unselect_all(PGtkIconView(MainView));
2144        end;
2145      end;
2146    end;
2147  finally
2148    Exclude(Widgets^.WidgetInfo^.Flags, wwiInvalidEvent);
2149  end;
2150end;
2151
2152class procedure TGtk2WSCustomListView.SetAllocBy(const ALV: TCustomListView;
2153  const AValue: Integer);
2154begin
2155  if not WSCheckHandleAllocated(ALV, 'SetAllocBy')
2156  then Exit;
2157end;
2158
2159class procedure TGtk2WSCustomListView.SetColor(const AWinControl: TWinControl);
2160var
2161  AWidget: PGTKWidget;
2162begin
2163  if not WSCheckHandleAllocated(AWinControl, 'SetColor') then
2164    Exit;
2165  AWidget := {%H-}PGtkWidget(AWinControl.Handle);
2166  AWidget := GetWidgetInfo(AWidget, True)^.CoreWidget;
2167  Gtk2WidgetSet.SetWidgetColor(AWidget,
2168    AWinControl.Font.Color,
2169    AWinControl.Color,
2170    [GTK_STATE_NORMAL, GTK_STATE_ACTIVE, GTK_STATE_PRELIGHT, GTK_STYLE_BASE]);
2171end;
2172
2173class procedure TGtk2WSCustomListView.SetDefaultItemHeight(
2174  const ALV: TCustomListView; const AValue: Integer);
2175begin
2176  if not WSCheckHandleAllocated(ALV, 'SetDefaultItemHeight')
2177  then Exit;
2178end;
2179
2180class procedure TGtk2WSCustomListView.SetFont(const AWinControl: TWinControl;
2181  const AFont: TFont);
2182var
2183  Widget: PGtkWidget;
2184begin
2185  if not WSCheckHandleAllocated(AWinControl, 'SetFont') then
2186    Exit;
2187  Widget := {%H-}PGtkWidget(AWinControl.Handle);
2188  Widget := GetWidgetInfo(Widget, True)^.CoreWidget;
2189  Gtk2WidgetSet.SetWidgetFont(Widget, AFont);
2190  Gtk2WidgetSet.SetWidgetColor(Widget, AFont.Color, clNone,
2191                              [GTK_STATE_NORMAL,GTK_STATE_ACTIVE,
2192                               GTK_STATE_PRELIGHT,GTK_STATE_SELECTED,
2193                               GTK_STYLE_TEXT]);
2194end;
2195
2196class procedure TGtk2WSCustomListView.SetHotTrackStyles(const ALV: TCustomListView;
2197  const AValue: TListHotTrackStyles);
2198begin
2199  if not WSCheckHandleAllocated(ALV, 'SetHotTrackStyles')
2200  then Exit;
2201end;
2202
2203class procedure TGtk2WSCustomListView.SetImageList(const ALV: TCustomListView;
2204  const AList: TListViewImageList; const AValue: TCustomImageListResolution);
2205var
2206  Widgets: PTVWidgets;
2207  BitImage: TBitmap;
2208  pixbuf: PGDKPixBuf;
2209  i: Integer;
2210  APGList: PGList;
2211  pixrenderer: PGtkCellRenderer;
2212begin
2213  if not WSCheckHandleAllocated(ALV, 'SetImageList')
2214  then Exit;
2215  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
2216  gtk_widget_queue_draw(Widgets^.MainView);
2217  if ((AList = lvilLarge) and (TLVHack(ALV).ViewStyle = vsIcon)) or
2218     ((AList = lvilSmall) and (TLVHack(ALV).ViewStyle <> vsIcon)) then
2219  begin
2220    if Widgets^.Images <> nil then
2221    begin
2222      for i := 0 to Widgets^.Images.Count-1 do
2223        gdk_pixbuf_unref(PGdkPixBuf(Widgets^.Images.Items[i]));
2224      Widgets^.Images.Clear;
2225    end;
2226    if AValue = nil then
2227      Exit;
2228    if Widgets^.Images = nil then
2229      Widgets^.Images := TList.Create;
2230
2231    if (AValue.Count = 0) and GTK_IS_TREE_VIEW(Widgets^.MainView) and (TLVHack(ALV).Columns.Count > 0) and
2232      not TLVHack(ALV).OwnerDraw then
2233    begin
2234      APGList := gtk_tree_view_column_get_cell_renderers(gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), 0));
2235      pixrenderer := PGtkCellRenderer(g_list_last(APGList)^.prev^.data);
2236      gtk_cell_renderer_set_fixed_size(pixrenderer, AValue.Width + 2, AValue.Height + 2);
2237      g_list_free(APGList);
2238      gtk_tree_view_column_queue_resize(gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), 0));
2239    end;
2240
2241    for i := 0 to AValue.Count-1 do
2242    begin
2243      pixbuf := nil;
2244      BitImage := TBitmap.Create;
2245      try
2246        AValue.GetBitmap(i, BitImage);
2247        Gtk2_PixBufFromBitmap(BitImage, pixbuf);
2248        if GTK_IS_TREE_VIEW(Widgets^.MainView) and (TLVHack(ALV).Columns.Count > 0) and
2249          not TLVHack(ALV).OwnerDraw then
2250        begin
2251          APGList := gtk_tree_view_column_get_cell_renderers(gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), 0));
2252          pixrenderer := PGtkCellRenderer(g_list_last(APGList)^.prev^.data);
2253          gtk_cell_renderer_set_fixed_size(pixrenderer, AValue.Width + 2, AValue.Height + 2);
2254          g_list_free(APGList);
2255          gtk_tree_view_column_queue_resize(gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), 0));
2256        end;
2257        Widgets^.Images.Add(pixbuf);
2258      finally
2259        BitImage.Free;
2260      end;
2261    end;
2262  end;
2263end;
2264
2265class procedure TGtk2WSCustomListView.SetItemsCount(const ALV: TCustomListView;
2266  const Avalue: Integer);
2267var
2268  Widgets: PTVWidgets;
2269  {$IFDEF USEORIGTREEMODEL}
2270  Iter: TGtkTreeIter;
2271  Index: Integer;
2272  {$ENDIF}
2273begin
2274  if not WSCheckHandleAllocated(ALV, 'SetItemsCount')
2275  then Exit;
2276
2277  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
2278
2279  with Widgets^ do
2280  begin
2281    {$IFNDEF USEORIGTREEMODEL}
2282    g_object_ref(TreeModel);
2283    gtk_tree_view_set_model(PGtkTreeView(MainView), nil);
2284    gtk_tree_view_set_model(PGtkTreeView(MainView), TreeModel);
2285    g_object_unref(TreeModel);
2286    {$ELSE}
2287    gtk_list_store_clear(PGtkListStore(TreeModel));
2288    for Index := 0 to AValue - 1 do
2289      gtk_list_store_insert_with_values(PGtkListStore(TreeModel), @Iter, Index, 0, nil, -1);
2290    {$ENDIF}
2291  end;
2292end;
2293
2294class procedure TGtk2WSCustomListView.SetProperty(const ALV: TCustomListView;
2295  const AProp: TListViewProperty; const AIsSet: Boolean);
2296var
2297  Widgets: PTVWidgets;
2298begin
2299  if not WSCheckHandleAllocated(ALV, 'SetProperty')
2300  then Exit;
2301
2302  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
2303
2304  SetPropertyInternal(ALV, Widgets, AProp, AIsSet);
2305end;
2306
2307class procedure TGtk2WSCustomListView.SetProperties(const ALV: TCustomListView;
2308  const AProps: TListViewProperties);
2309var
2310  Widgets: PTVWidgets;
2311  Prop: TListViewProperty;
2312begin
2313  if not WSCheckHandleAllocated(ALV, 'SetProperties')
2314  then Exit;
2315
2316  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
2317
2318  for Prop := Low(Prop) to High(Prop) do
2319    SetPropertyInternal(ALV, Widgets, Prop, Prop in AProps);
2320end;
2321
2322class procedure TGtk2WSCustomListView.SetScrollBars(const ALV: TCustomListView;
2323  const AValue: TScrollStyle);
2324var
2325  SS:TPoint;
2326  ScrollWidget: PGtkScrolledWindow;
2327begin
2328  if not WSCheckHandleAllocated(ALV, 'SetScrollBars') then
2329    exit;
2330
2331  ScrollWidget := {%H-}PGtkScrolledWindow(ALV.Handle);
2332  SS := Gtk2TranslateScrollStyle(AValue);
2333  gtk_scrolled_window_set_policy(ScrollWidget ,SS.X, SS.Y);
2334end;
2335
2336class procedure TGtk2WSCustomListView.SetSort(const ALV: TCustomListView;
2337  const AType: TSortType; const AColumn: Integer;
2338  const ASortDirection: TSortDirection);
2339var
2340  Widgets: PTVWidgets;
2341begin
2342  if not WSCheckHandleAllocated(ALV, 'SetSort')
2343  then Exit;
2344  // gtk2 needs only update
2345  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
2346  if GetViewModel(Widgets^.MainView) = nil then
2347    exit;
2348
2349  if not gtk_widget_realized(Widgets^.MainView) then
2350    exit;
2351
2352  if GTK_IS_TREE_VIEW(Widgets^.MainView) then
2353    gtk_widget_queue_draw(Widgets^.MainView);
2354end;
2355
2356class procedure TGtk2WSCustomListView.SetViewOrigin(const ALV: TCustomListView;
2357  const AValue: TPoint);
2358var
2359  Widgets: PTVWidgets;
2360begin
2361  if not WSCheckHandleAllocated(ALV, 'SetViewOrigin')
2362  then Exit;
2363
2364  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets{%H-});
2365  //DebugLn(['TGtk2WSCustomListView.SetViewOrigin ',GetWidgetDebugReport(Widgets^.MainView)]);
2366  if not GTK_WIDGET_REALIZED(Widgets^.MainView) then exit;
2367  if GTK_IS_TREE_VIEW(Widgets^.MainView) then
2368    gtk_tree_view_scroll_to_point(PGtkTreeView(Widgets^.MainView), AValue.X, AValue.Y)
2369  else
2370  if GTK_IS_ICON_VIEW(Widgets^.MainView) then
2371  begin
2372    // TODO: iconview
2373  end;
2374end;
2375
2376class procedure TGtk2WSCustomListView.SetViewStyle(const ALV: TCustomListView;
2377  const AValue: TViewStyle);
2378
2379var
2380  APtrIntData: PtrInt;
2381
2382  procedure ShowColumns(const Widgets: PTVWidgets; const Show: Boolean);
2383  var
2384    List: PGList;
2385    GtkColumn: PGtkTreeViewColumn;
2386    i: Integer;
2387  begin
2388    if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
2389      Exit;
2390    List := gtk_tree_view_get_columns(PGtkTreeView(Widgets^.MainView));
2391    for i := 0 to g_list_length(List) - 1 do
2392    begin
2393      GtkColumn := g_list_nth_data(List, i);
2394      if GtkColumn = nil then
2395        Continue;
2396
2397      if not Show or
2398        (Show and ({%H-}PtrUInt(g_object_get_data(G_OBJECT(GtkColumn),
2399                                             PChar('Visible'))) <> 0)) then
2400        gtk_tree_view_column_set_visible(GtkColumn, Show);
2401    end;
2402    g_list_free(List)
2403  end;
2404
2405var
2406  Widgets: PTVWidgets;
2407begin
2408  if not WSCheckHandleAllocated(ALV, 'SetViewStyle')
2409  then Exit;
2410
2411  GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets{%H-});
2412  if g_object_get_data(PGObject(Widgets^.MainView),'lcllistviewstyle') <> nil then
2413    APtrIntData := {%H-}PtrInt(g_object_get_data(PGObject(Widgets^.MainView),'lcllistviewstyle'))
2414  else
2415    APtrIntData := -1;
2416  if (APtrIntData <> -1) and (APtrIntData - 1 <> Ord(AValue)) then
2417  begin
2418    // we have to free the GtkTreeView and Create GtkIconView etc depending on the new style
2419    //RecreateMainView(ALV);
2420    // we actually need to recreate our ListView since not only the widget changes but also columns
2421    RecreateWnd(ALV);
2422    Exit;
2423  end;
2424
2425  ShowColumns(Widgets, AValue = vsReport);
2426  with Widgets^ do
2427  begin
2428    case AValue of
2429      vsIcon,
2430      vsSmallIcon:
2431        begin
2432          SetNeedDefaultColumn(ALV, True);
2433        end;
2434      vsList:
2435        begin
2436          SetNeedDefaultColumn(ALV, True);
2437          gtk_tree_view_set_headers_visible(GTK_TREE_VIEW (MainView), False);
2438        end;
2439      vsReport:
2440        begin
2441          SetNeedDefaultColumn(ALV, False);
2442          if TLVHack(ALV).ShowColumnHeaders = True then
2443            gtk_tree_view_set_headers_visible(GTK_TREE_VIEW (MainView), True);
2444        end;
2445    end;
2446  end;
2447//  inherited SetViewStyle(ALV, Avalue);
2448end;
2449