lazarus/lcl/interfaces/gtk2/gtk2wscustomlistview.inc

2475 lines
79 KiB
PHP

{%MainUnit gtk2wscomctrls.pp}
{
*****************************************************************************
This file is part of the Lazarus Component Library (LCL)
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
}
type
TLVHack = class(TCustomListView)
end;
TLVItemHack = class(TListItem)
end;
////////////////////////////////////////
//// Event Code /////////////////////
////////////////////////////////////////
function IsOldGtk2: Boolean;
begin
Result := (gtk_major_version = 2) and (gtk_minor_version < 10);
end;
procedure Gtk2_PixBufFromBitmap(const BitImage:TBitmap; out pixbuf:PGdkPixbuf);
var
GDIObj: PGDIObject;
bitmap:PGdkBitmap;
Width, Height:gint;
pixmap: PGdkPixmap;
begin
GDIObj := {%H-}PGDIObject(BitImage.Handle);
case GDIObj^.GDIBitmapType of
gbBitmap:
begin
bitmap := GDIObj^.GDIBitmapObject;
gdk_drawable_get_size(bitmap, @Width, @Height);
pixbuf := CreatePixbufFromDrawable(bitmap, nil, False, 0, 0, 0, 0, Width, Height);
end;
gbPixmap:
begin
pixmap := GDIObj^.GDIPixmapObject.Image;
if pixmap <> nil then
begin
gdk_drawable_get_size(pixmap, @Width, @Height);
bitmap := CreateGdkMaskBitmap(BitImage.Handle, 0);
pixbuf := CreatePixbufFromImageAndMask(pixmap, 0, 0, Width, Height, nil, Bitmap);
end;
end;
gbPixbuf:
begin
pixbuf := gdk_pixbuf_copy(GDIObj^.GDIPixbufObject);
end;
end;
end;
procedure Gtk2_ItemCheckedChanged(renderer: PGtkCellRendererToggle; PathStr: Pgchar; WidgetInfo: PWidgetInfo);cdecl;
var
LV: TLVHack;
Index: Integer;
ListItem: TLVItemHack;
ARect: TGdkRectangle;
R: TRect;
x, y, cellw, cellh: gint;
begin
LV := TLVHack(WidgetInfo^.LCLObject);
Index := StrToInt(PathStr);
ListItem := TLVItemHack(LV.Items.Item[Index]);
if ListItem <> nil then
begin
ListItem.Checked := not ListItem.GetCheckedInternal;
if Assigned(LV.OnItemChecked) then
LV.OnItemChecked(TListView(WidgetInfo^.LCLObject), LV.Items.Item[Index]);
// we must update renderer row, otherwise visually it looks different
// if we change toggle state by keyboard (eg. pressing Space key)
R := ListItem.DisplayRect(drBounds);
ARect := GdkRectFromRect(R);
gtk_cell_renderer_get_size(PGtkCellRenderer(renderer),
WidgetInfo^.CoreWidget, @ARect, @x,@y, @cellw, @cellh);
with R do
gtk_widget_queue_draw_area(WidgetInfo^.CoreWidget, Left, Top, cellW, cellH);
end;
end;
procedure Gtk2_ItemFocusChanged(Widget: PGtkWidget; WidgetInfo: PWidgetInfo);cdecl;
var
msg: TLMNotify;
NM: TNMListView;
path: PGtkTreePath;
pstr: PChar;
column: PGtkTreeViewColumn;
cell: PGtkCellRenderer;
begin
// DebugLn('Gtk2_ItemFocusChanged');
// the defocus of the oldrow isn't send
if GTK_IS_TREE_VIEW(Widget) then begin
path:=nil;
column:=nil;
gtk_tree_view_get_cursor(PGtkTreeView(Widget), path, column);
end
else
if GTK_IS_ICON_VIEW(Widget) then begin
path:=nil;
cell:=nil;
gtk_icon_view_get_cursor(PGtkIconView(Widget), path, cell);
end
else
path := nil;
if path = nil then
Exit;
gtk_tree_path_free(path);
msg.Msg := CN_NOTIFY;
FillChar(NM{%H-}, SizeOf(NM), 0);
NM.hdr.hwndfrom := {%H-}PtrUInt(WidgetInfo^.CoreWidget);
NM.hdr.code := LVN_ITEMCHANGED;
pstr:=gtk_tree_path_to_string(path);
NM.iItem := StrToInt(pstr);
g_free(pstr);
NM.iSubItem := 0;
NM.uNewState := LVIS_FOCUSED;
NM.uChanged := LVIF_STATE;
msg.NMHdr := @NM.hdr;
DeliverMessage(WidgetInfo^.LCLObject, msg);
end;
procedure Gtk2_ItemDeleted({%H-}model: PGtkTreeModel; path: PGtkTreePath; WidgetInfo: PWidgetInfo); cdecl;
var
msg: TLMNotify;
NM: TNMListView;
pstr:PChar;
begin
//DebugLn('Gtk2_ItemDeleted');
msg.Msg := CN_NOTIFY;
FillChar(NM{%H-}, SizeOf(NM), 0);
NM.hdr.hwndfrom := {%H-}PtrUInt(WidgetInfo^.CoreWidget);
NM.hdr.code := LVN_DELETEITEM;
pstr := gtk_tree_path_to_string(path);
NM.iItem := StrToInt(pstr);
g_free(pstr);
msg.NMHdr := @NM.hdr;
DeliverMessage(WidgetInfo^.LCLObject, msg);
end;
procedure Gtk2_ItemInserted({%H-}model: PGtkTreeModel; path: PGtkTreePAth; {%H-}Iter: PGtkTreeIter; WidgetInfo: PWidgetInfo); cdecl;
var
msg: TLMNotify;
NM: TNMListView;
begin
//DebugLn('Gtk2_ItemInserted');
msg.Msg := CN_NOTIFY;
FillChar(NM{%H-}, SizeOf(NM), 0);
NM.hdr.hwndfrom := {%H-}PtrUInt(WidgetInfo^.CoreWidget);
NM.hdr.code := LVN_INSERTITEM;
NM.iItem := gtk_tree_path_get_indices(path)^;
msg.NMHdr := @NM.hdr;
DeliverMessage(WidgetInfo^.LCLObject, msg);
end;
//This is only for when the tree view sorts itself. Not needed since the LCL does the sorting.
//procedure Gtk2_ItemMoved(model: PGtkTreeModel; path: PGtkTreePAth; Iter: PGtkTreeIter; WidgetInfo: PWidgetInfo); cdecl;
//begin
//end;
procedure Gtk2_ItemChanged({%H-}model: PGtkTreeModel; {%H-}path: PGtkTreePAth; {%H-}Iter: PGtkTreeIter; {%H-}WidgetInfo: PWidgetInfo); cdecl;
begin
// OnChange Occurs immediately after an item in the list changes.
// The Item parameter is the list item that just changed. The Change parameter
// indicates the type of change that just occurred. Change is ctText if the
// Caption property of the item changed. Change is ctImage if the ImageIndex
// property of the item changed or the appropriate image list changed in the
// list view. Change is ctState if the Cut, Focused, or Selected property of
// the item changed.
// DebugLn('Gtk2_ItemChanged');
end;
procedure Gtk2_ColumnClicked(column: PGtkTreeViewColumn; WidgetInfo: PWidgetInfo); cdecl;
var
AColumn: TListColumn;
msg: TLMNotify;
NM: TNMListView;
begin
AColumn := TListColumn(g_object_get_data(G_OBJECT(column), 'TListColumn'));
msg.Msg := CN_NOTIFY;
FillChar(NM{%H-}, SizeOf(NM), 0);
NM.hdr.hwndfrom := {%H-}PtrUInt(WidgetInfo^.CoreWidget);
NM.hdr.code := LVN_COLUMNCLICK;
NM.iItem := -1;
NM.iSubItem := AColumn.Index;
msg.NMHdr := @NM.hdr;
DeliverMessage(WidgetInfo^.LCLObject, msg);
end;
procedure BroadcastListSelection(Target : Pointer; AHandle : HWND; AIndex : Integer;
AState : Boolean);
var
msg: TLMNotify;
NM: TNMListView;
begin
msg.Msg := CN_NOTIFY;
FillChar(NM{%H-}, SizeOf(NM), 0);
NM.hdr.hwndfrom := AHandle;
NM.hdr.code := LVN_ITEMCHANGED;
NM.iItem := AIndex;
NM.iSubItem := 0;
if AState then
NM.uOldState := LVIS_SELECTED
else
NM.uNewState := LVIS_SELECTED;
NM.uChanged := LVIF_STATE;
msg.NMHdr := @NM.hdr;
if g_object_get_data({%H-}PGObject(TWinControl(Target).Handle),'lcl_gtkwidget_in_update') = nil then
begin
DeliverMessage(Target, msg);
if TLVHack(Target).Selected = TLVHack(Target).Items[AIndex] then
begin
NM.uOldState := 0;
NM.uNewState := LVIS_FOCUSED;
NM.uChanged := LVIF_STATE;
msg.NMHdr := @NM.hdr;
DeliverMessage(Target, msg);
end else
if TLVHack(Target).Selected = nil then
begin
NM.uOldState := LVIS_FOCUSED;
NM.uNewState := 0;
NM.uChanged := LVIF_STATE;
msg.NMHdr := @NM.hdr;
DeliverMessage(Target, msg);
end;
end;
end;
procedure ChangeItemCache(const AWidgets: PTVWidgets; const AItem: Integer; const AState: TTVItemState);
begin
if Length(AWidgets^.ItemCache) <= AItem then
SetLength(AWidgets^.ItemCache, AItem + TVItemCachePart);
if AWidgets^.ItemCacheCount <= AItem then
AWidgets^.ItemCacheCount := AItem + 1;
AWidgets^.ItemCache[AItem] := AState;
end;
procedure BroadcastItemCache(const AWidgets: PTVWidgets; const AWidgetInfo: PWidgetInfo);
var
i: Integer;
begin
for i := 0 to AWidgets^.ItemCacheCount -1 do
if AWidgets^.ItemCache[i] = tvisSelected then
BroadcastListSelection(AWidgetInfo^.LCLObject, {%H-}PtrUInt(AWidgets^.MainView),
i, True);
for i := 0 to AWidgets^.ItemCacheCount -1 do
if AWidgets^.ItemCache[i] = tvisUnselected then
BroadcastListSelection(AWidgetInfo^.LCLObject, {%H-}PtrUInt(AWidgets^.MainView),
i, False);
if Length(AWidgets^.ItemCache) > TVItemCachePart then
SetLength(AWidgets^.ItemCache, TVItemCachePart);
FillByte(AWidgets^.ItemCache[0], Length(AWidgets^.ItemCache), Ord(tvisUndefined));
AWidgets^.ItemCacheCount := 0;
end;
procedure Gtk2_ItemSelectionChanged(selection: PGtkTreeSelection; WidgetInfo: PWidgetInfo); cdecl;
var
Widgets: PTVWidgets;
AIndex: String;
i: Integer;
Indices: Integer;
ListIndex: Integer;
List: PgList;
Path: PGtkTreePath;
begin
// DebugLn('Gtk2_ItemSelectionChanged');
Widgets := PTVWidgets(WidgetInfo^.UserData);
if Widgets = nil then
Exit;
if wwiInvalidEvent in Widgets^.WidgetInfo^.Flags then
exit;
if Length(Widgets^.ItemCache)=0 then
begin
// debugln(' Gtk2_ItemSelectionChanged ItemCache=nil ',tComponent(widgetInfo^.lclObject).name);
if IsOldGtk2 and (Widgets <> nil) then
begin
// debugLn('ItemsCache is valid ! count ',dbgs(Widgets^.ItemCache.Count));
List := gtk_tree_selection_get_selected_rows(Selection, nil);
if (List <> nil) then
begin
if Assigned(Widgets^.OldTreeSelection) then
begin
// we must iterate because of multiselections
for i := 0 to g_list_length(Widgets^.OldTreeSelection) - 1 do
begin
Path := g_list_nth_data(Widgets^.OldTreeSelection, i);
if Path <> nil then
begin
Indices := gtk_tree_path_get_indices(Path)^;
ChangeItemCache(Widgets, Indices, tvisSelected);
end;
end;
g_list_free(Widgets^.OldTreeSelection);
Widgets^.OldTreeSelection := g_list_alloc;
// we must iterate because of multiselections
for i := 0 to g_list_length(List) - 1 do
g_list_append(Widgets^.OldTreeSelection, g_list_nth_data(List, i));
end;
// now compare new selection (add or set as selected)
for i := 0 to g_list_length(List) - 1 do
begin
Path := g_list_nth_data(List, i);
if Path <> nil then
begin
Indices := gtk_tree_path_get_indices(Path)^;
ChangeItemCache(Widgets, Indices, tvisUnselected);
end;
end;
g_list_free(List);
end else
begin
// complete selection is clear !
if Assigned(Widgets^.OldTreeSelection) then
begin
for i := 0 to g_list_length(Widgets^.OldTreeSelection) - 1 do
begin
Path := g_list_nth_data(Widgets^.OldTreeSelection, i);
if Path <> nil then
begin
Indices := gtk_tree_path_get_indices(Path)^;
ChangeItemCache(Widgets, Indices, tvisSelected);
end;
end;
g_list_free(Widgets^.OldTreeSelection);
Widgets^.OldTreeSelection := g_list_alloc;
end;
end;
end else
Exit;
end;
// DebugLn('Gtk2_ItemSelectionChanged Trigger OnSelectItem ? ', dbgs(not (wwiInvalidEvent in Widgets^.WidgetInfo^.Flags)));
// LCL sent selection !
BroadcastItemCache(Widgets,WidgetInfo);
end;
procedure Gtk2_IconViewSelectionChanged(AIconView: PGtkIconView; WidgetInfo: PWidgetInfo); cdecl;
var
Widgets: PTVWidgets;
AIndex: String;
i: Integer;
List: PGList;
Path: PGtkTreePath;
pstr: PChar;
Indices: integer;
begin
Widgets := PTVWidgets(WidgetInfo^.UserData);
List := gtk_icon_view_get_selected_items(AIconView);
if (List <> nil) then
begin
Path := PGtkTreePath(g_list_first(List)^.data);
Indices := gtk_tree_path_get_indices(path)^;
ChangeItemCache(Widgets, Indices, tvisUnselected);
g_list_free(List);
end else
exit;
// LCL already sent selection !
if wwiInvalidEvent in Widgets^.WidgetInfo^.Flags then
exit;
BroadcastItemCache(Widgets,WidgetInfo);
end;
function Gtk2WSLV_ItemSelected({%H-}selection: PGtkTreeSelection; {%H-}model: PGtkTreeModel;
path: PGtkTreePath; path_is_currently_selected: GBoolean; WidgetInfo: PWidgetInfo): GBoolean; cdecl;
var
Widgets: PTVWidgets;
Indices: integer;
begin
// DebugLn('Gtk2_ItemSelected ');
// this function is called *before* the item is selected
// The result should be True to allow the Item to change selection
Result := True;
Widgets := PTVWidgets(WidgetInfo^.UserData);
if wwiInvalidEvent in Widgets^.WidgetInfo^.Flags then
exit;
if Widgets <> nil then
begin
Indices := gtk_tree_path_get_indices(path)^;
if path_is_currently_selected then
ChangeItemCache(Widgets, Indices, tvisSelected)
else
ChangeItemCache(Widgets, Indices, tvisUnselected);
end;
end;
procedure Gtk2WSLV_ListViewGetCheckedDataFunc({%H-}tree_column: PGtkTreeViewColumn;
cell: PGtkCellRenderer; tree_model: PGtkTreeModel; iter: PGtkTreeIter; WidgetInfo: PWidgetInfo); cdecl;
var
APath: PGtkTreePath;
ListItem: TLVItemHack;
begin
gtk_tree_model_get(tree_model, iter, [0, @ListItem, -1]);
if (ListItem = nil) and TCustomListView(WidgetInfo^.LCLObject).OwnerData then
begin
APath := gtk_tree_model_get_path(tree_model,iter);
ListItem := TLVItemHack(TCustomListView(WidgetInfo^.LCLObject).Items[gtk_tree_path_get_indices(APath)^]);
gtk_tree_path_free(APath);
end;
if ListItem = nil then
Exit;
gtk_cell_renderer_toggle_set_active(PGtkCellRendererToggle(cell), ListItem.GetCheckedInternal);
end;
procedure Gtk2WSLV_ListViewGetPixbufDataFuncForColumn(tree_column: PGtkTreeViewColumn;
cell: PGtkCellRenderer; tree_model: PGtkTreeModel; iter: PGtkTreeIter; WidgetInfo: PWidgetInfo); cdecl;
var
ListItem: TListItem;
Images: TList;
Widgets: PTVWidgets;
ListColumn: TListColumn;
ImageIndex: Integer;
ColumnIndex: Integer;
APath: PGtkTreePath;
ImageList: TCustomImageList;
Bmp: TBitmap;
pixbuf: PGdkPixbuf;
PixbufValue: TGValue;
begin
PixbufValue:=Default(TGValue);
g_value_init(@PixbufValue, GDK_TYPE_PIXBUF);
g_object_set_property(G_OBJECT(cell), PChar('pixbuf'), @PixbufValue);
g_value_unset(@PixbufValue);
Widgets := PTVWidgets(WidgetInfo^.UserData);
gtk_tree_model_get(tree_model, iter, [0, @ListItem, -1]);
ListColumn := TListColumn(g_object_get_data(G_OBJECT(tree_column), 'TListColumn'));
if ListColumn = nil then
Exit;
ColumnIndex := ListColumn.Index;
ImageList := nil;
Images := Widgets^.Images;
if TCustomListView(WidgetInfo^.LCLObject).OwnerData then
ImageList := TLVHack(WidgetInfo^.LCLObject).SmallImages;
if (Images = nil) and (ImageList = nil) then
begin
Exit;
end;
ImageIndex := -1;
if (ListItem = nil) and TCustomListView(WidgetInfo^.LCLObject).OwnerData then
begin
APath := gtk_tree_model_get_path(tree_model,iter);
ListItem := TCustomListView(WidgetInfo^.LCLObject).Items[gtk_tree_path_get_indices(APath)^];
gtk_tree_path_free(APath);
end;
if ListItem = nil then
Exit;
if ColumnIndex = 0 then
ImageIndex := ListItem.ImageIndex
else
if ColumnIndex -1 <= ListItem.SubItems.Count-1 then
ImageIndex := ListItem.SubItemImages[ColumnIndex-1];
if (ImageList <> nil) and
(ImageIndex > -1) and (ImageIndex <= ImageList.Count-1) then
begin
Bmp := TBitmap.create;
try
pixbuf := nil;
ImageList.GetBitmap(ImageIndex, Bmp);
Gtk2_PixBufFromBitmap(Bmp,pixbuf);
g_value_init(@PixbufValue, GDK_TYPE_PIXBUF);
g_value_set_object(@PixbufValue, pixbuf);
g_object_set_property(G_OBJECT(cell), PChar('pixbuf'), @PixbufValue);
g_value_unset(@PixbufValue);
finally
Bmp.Free;
end;
end else
g_value_init(@PixbufValue, GDK_TYPE_PIXBUF);
if (ImageIndex > -1) and (ImageIndex <= Images.Count-1) and (Images.Items[ImageIndex] <> nil) then
g_value_set_object(@PixbufValue, PGdkPixbuf(Images.Items[ImageIndex]));
g_object_set_property(G_OBJECT(cell), PChar('pixbuf'), @PixbufValue);
g_value_unset(@PixbufValue);
end;
procedure Gtk2WSLV_ListViewGetPixbufDataFuncForIconView({%H-}cell_layout:PGtkCellLayout;
cell: PGtkCellRenderer; tree_model: PGtkTreeModel; iter: PGtkTreeIter; WidgetInfo: PWidgetInfo); cdecl;
var
ListItem: TListItem;
Images: TList;
Widgets: PTVWidgets;
ImageIndex: Integer;
APath: PGtkTreePath;
PixbufValue: TGValue;
begin
PixbufValue:=Default(TGValue);
g_value_init(@PixbufValue, GDK_TYPE_PIXBUF);
g_object_set_property(G_OBJECT(cell), 'pixbuf', @PixbufValue);
g_value_unset(@PixbufValue);
Widgets := PTVWidgets(WidgetInfo^.UserData);
gtk_tree_model_get(tree_model, iter, [0, @ListItem, -1]);
Images := Widgets^.Images;
if Images = nil then
Exit;
ImageIndex := -1;
if (ListItem = nil) and TCustomListView(WidgetInfo^.LCLObject).OwnerData then
begin
APath := gtk_tree_model_get_path(tree_model,iter);
ListItem := TCustomListView(WidgetInfo^.LCLObject).Items[gtk_tree_path_get_indices(APath)^];
gtk_tree_path_free(APath);
end;
if ListItem = nil then
Exit;
ImageIndex := ListItem.ImageIndex;
g_value_init(@PixbufValue, GDK_TYPE_PIXBUF);
if (ImageIndex > -1) and (ImageIndex <= Images.Count-1) then
g_value_set_object(@PixbufValue, PGdkPixbuf(Images.Items[ImageIndex]));
g_object_set_property(G_OBJECT(cell), PChar('pixbuf'), @PixbufValue);
g_value_unset(@PixbufValue);
end;
{ TGtk2WSCustomListView }
class procedure TGtk2WSCustomListView.SetPropertyInternal(const ALV: TCustomListView;
const Widgets: PTVWidgets; const AProp: TListViewProperty;
const AIsSet: Boolean);
const
BoolToSelectionMode: array[Boolean] of TGtkSelectionMode = (
GTK_SELECTION_SINGLE,
GTK_SELECTION_MULTIPLE
);
begin
with Widgets^ do begin
case AProp of
lvpAutoArrange: begin
// TODO: implement ??
end;
lvpCheckboxes:
begin
if TLVHack(ALV).ViewStyle in [vsReport,vsList] then
AddRemoveCheckboxRenderer(ALV, GetWidgetInfo(Widgets^.MainView), AIsSet);
end;
lvpColumnClick: begin
// allow only column modifications when in report mode
if TLVHack(ALV).ViewStyle <> vsReport then Exit;
gtk_tree_view_set_headers_clickable(PGtkTreeView(MainView), AIsSet);
end;
lvpFlatScrollBars: begin
// TODO: implement ??
end;
lvpFullDrag: begin
// TODO: implement ??
end;
lvpGridLines: begin
// TODO: better implementation
// maybe possible with some cellwidget hacking
// this create rows with alternating colors
if GTK_IS_TREE_VIEW(MainView) then
begin
if gtk_tree_view_set_grid_lines <> nil then
begin
if AIsSet then
gtk_tree_view_set_grid_lines(PGtkTreeView(MainView), GTK_TREE_VIEW_GRID_LINES_BOTH)
else
gtk_tree_view_set_grid_lines(PGtkTreeView(MainView), GTK_TREE_VIEW_GRID_LINES_NONE);
end else
gtk_tree_view_set_rules_hint(PGtkTreeView(MainView), AIsSet);
end;
end;
lvpHideSelection: begin
// TODO: implement
// should be possible with some focus in/out events
end;
lvpHotTrack: begin
// TODO: implement
// should be possible with some mouse tracking
end;
lvpMultiSelect: begin
if GTK_IS_TREE_VIEW(MainView) then
gtk_tree_selection_set_mode(TreeSelection, BoolToSelectionMode[AIsSet])
else
if GTK_IS_ICON_VIEW(MainView) then
gtk_icon_view_set_selection_mode(PGtkIconView(MainView), BoolToSelectionMode[AIsSet]);
end;
lvpOwnerDraw:
begin
// It must send CN_DRAWITEM with ItemID and proper rect of item
// then LCL does all other stuff. Note that OwnerDraw should work only
// in case of vsReport, according to embarcadero docs.
// http://docwiki.embarcadero.com/Libraries/XE4/en/Vcl.ComCtrls.TCustomListView.OnDrawItem
// NOTE: this is automatically handled by cell renderer (Gtk2CellRenderer).
end;
lvpReadOnly: begin
// TODO: implement inline editor ?
end;
lvpRowSelect: begin
// TODO: implement ???
// how to do cell select
end;
lvpShowColumnHeaders: begin
// allow only column modifications when in report mode
if TLVHack(ALV).ViewStyle <> vsReport then Exit;
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW (MainView), AIsSet);
end;
lvpShowWorkAreas: begin
// TODO: implement ???
end;
lvpWrapText: begin
// TODO: implement ???
end;
end;
end;
end;
class procedure TGtk2WSCustomListView.SetNeedDefaultColumn(const ALV: TCustomListView; const AValue: Boolean);
var
Widgets: PTVWidgets;
WidgetInfo: PWidgetInfo;
GtkColumn: PGtkTreeViewColumn;
pixrenderer,
textrenderer: PGtkCellRenderer;
begin
if not WSCheckHandleAllocated(ALV, 'SetNeedDefaultColumn')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
WidgetInfo := GetWidgetInfo({%H-}PGtkWidget(ALV.Handle));
GtkColumn := g_object_get_data(G_OBJECT(Widgets^.MainView), 'LCL_DEFAULT_COLUMN');
if AValue then
begin
if GtkColumn = nil then
begin
GtkColumn := gtk_tree_view_column_new();
gtk_widget_unset_flags(PGtkWidget(GtkColumn), GTK_CAN_FOCUS);
// add renderers
pixrenderer := gtk_cell_renderer_pixbuf_new();
textrenderer := LCLIntfCellRenderer_New;
if GTK_IS_TREE_VIEW(Widgets^.MainView) then
begin
gtk_tree_view_column_pack_start(GtkColumn, pixrenderer, FALSE);
//gtk_tree_view_column_set_attributes(GtkColumn, pixrenderer,['pixbuf', 0, nil]);
gtk_tree_view_column_pack_start(GtkColumn, textrenderer, True);
//gtk_tree_view_column_set_attributes(GtkColumn, textrenderer, ['text',0, nil]);
gtk_tree_view_column_set_cell_data_func(GtkColumn, pixrenderer, TGtkTreeCellDataFunc(@Gtk2WSLV_ListViewGetPixbufDataFuncForColumn), WidgetInfo, nil);
gtk_tree_view_column_set_cell_data_func(GtkColumn, textrenderer, TGtkTreeCellDataFunc(@LCLIntfCellRenderer_CellDataFunc), WidgetInfo, nil);
// insert column
gtk_tree_view_insert_column(GTK_TREE_VIEW(Widgets^.MainView), GtkColumn, 0);
end
else
if GTK_IS_ICON_VIEW(Widgets^.MainView) then
begin
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(Widgets^.MainView), pixrenderer, False);
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(Widgets^.MainView), textrenderer, True);
gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(Widgets^.MainView), pixrenderer, TGtkCellLayoutDataFunc(@Gtk2WSLV_ListViewGetPixbufDataFuncForIconView), WidgetInfo, nil);
gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(Widgets^.MainView), textrenderer, TGtkCellLayoutDataFunc(@LCLIntfCellRenderer_CellDataFunc), WidgetInfo, nil);
end;
g_object_set_data(G_OBJECT(Widgets^.MainView), 'LCL_DEFAULT_COLUMN', GtkColumn);
end;
end
else begin // No Column Needed
if GtkColumn <> nil then
begin
if GTK_IS_TREE_VIEW(Widgets^.MainView) and GTK_IS_TREE_VIEW_COLUMN(GtkColumn) then
gtk_tree_view_remove_column(PGtkTreeView(Widgets^.MainView), GtkColumn)
else
if GTK_IS_TREE_VIEW_COLUMN(GtkColumn) and G_IS_OBJECT(GtkColumn) then
g_object_unref(GtkColumn);
g_object_set_data(G_OBJECT(Widgets^.MainView), 'LCL_DEFAULT_COLUMN', nil);
end;
end;
end;
class procedure TGtk2WSCustomListView.AddRemoveCheckboxRenderer(
const ALV: TCustomListView; const WidgetInfo: PWidgetInfo; const Add: Boolean);
var
togglerenderer,
pixrenderer,
textrenderer: PGtkCellRenderer;
column: PGtkTreeViewColumn;
renderers: PGList;
begin
column := gtk_tree_view_get_column(PGtkTreeView(WidgetInfo^.CoreWidget), 0);
if column = nil then
Exit;
renderers := gtk_tree_view_column_get_cell_renderers(column);
textrenderer := PGtkCellRenderer(g_list_last(renderers)^.data);
pixrenderer := PGtkCellRenderer(g_list_last(renderers)^.prev^.data);
g_list_free(renderers);
g_object_ref(G_OBJECT(pixrenderer));
g_object_ref(G_OBJECT(textrenderer));
if Add then
begin
gtk_cell_layout_clear(GTK_CELL_LAYOUT(column));
togglerenderer := gtk_cell_renderer_toggle_new();
gtk_tree_view_column_pack_start(column, togglerenderer, FALSE);
gtk_tree_view_column_pack_start(column, pixrenderer, FALSE);
gtk_tree_view_column_pack_start(column, textrenderer, True);
gtk_tree_view_column_set_cell_data_func(column, togglerenderer, TGtkTreeCellDataFunc(@Gtk2WSLV_ListViewGetCheckedDataFunc), WidgetInfo, nil);
gtk_tree_view_column_set_cell_data_func(column, pixrenderer, TGtkTreeCellDataFunc(@Gtk2WSLV_ListViewGetPixbufDataFuncForColumn), WidgetInfo, nil);
gtk_tree_view_column_set_cell_data_func(column, textrenderer, TGtkTreeCellDataFunc(@LCLIntfCellRenderer_CellDataFunc), WidgetInfo, nil);
// connect toggled signal
g_signal_connect(togglerenderer, 'toggled', TGTKSignalFunc(@Gtk2_ItemCheckedChanged), GetWidgetInfo({%H-}PGtkWidget(ALV.Handle)));
end
else
begin
gtk_cell_layout_clear(GTK_CELL_LAYOUT(column));
gtk_tree_view_column_pack_start(column, pixrenderer, FALSE);
gtk_tree_view_column_pack_start(column, textrenderer, True);
gtk_tree_view_column_set_cell_data_func(column, pixrenderer, TGtkTreeCellDataFunc(@Gtk2WSLV_ListViewGetPixbufDataFuncForColumn), WidgetInfo, nil);
gtk_tree_view_column_set_cell_data_func(column, textrenderer, TGtkTreeCellDataFunc(@LCLIntfCellRenderer_CellDataFunc), WidgetInfo, nil);
end;
if G_IS_OBJECT(pixrenderer) then
g_object_unref(G_OBJECT(pixrenderer));
if G_IS_OBJECT(textrenderer) then
g_object_unref(G_OBJECT(textrenderer));
end;
class function TGtk2WSCustomListView.GetViewModel(const AView: PGtkWidget): PGtkTreeModel;
begin
if GTK_IS_TREE_VIEW(AView) then
Result := gtk_tree_view_get_model(PGtkTreeView(AView))
else
if GTK_IS_ICON_VIEW(AView) then
Result := gtk_icon_view_get_model(PGtkIconView(AView))
else
Result := nil;
end;
class procedure TGtk2WSCustomListView.SetListCallbacks(const AScrollWidget: PGtkWidget;
const Widgets: PTVWidgets; const AWidgetInfo: PWidgetInfo);
begin
TGtk2WSBaseScrollingWinControl.SetCallbacks(AScrollWidget, AWidgetInfo);
TGtk2WSWinControl.SetCallbacks(PGtkObject(Widgets^.MainView), TComponent(AWidgetInfo^.LCLObject));
// the callbacks for OnColumnClick are set when the column is created in ColumnInsert
if GTK_IS_TREE_VIEW(Widgets^.MainView) then
begin
if IsOldGtk2 then
// bug in 2.8, issue #19820
else
gtk_tree_selection_set_select_function(Widgets^.TreeSelection,TGtkTreeSelectionFunc(@Gtk2WSLV_ItemSelected), gpointer(AWidgetInfo),nil);
SignalConnect(PGtkWidget(Widgets^.TreeSelection), 'changed', @Gtk2_ItemSelectionChanged, AWidgetInfo);
SignalConnect(PGtkWidget(Widgets^.MainView), 'toggle-cursor-row', @Gtk2_ItemFocusChanged, AWidgetInfo);
end
else
if GTK_IS_ICON_VIEW(Widgets^.MainView) then
begin
SignalConnect(Widgets^.MainView, 'selection-changed', @Gtk2_IconViewSelectionChanged, AWidgetInfo);
SignalConnect(Widgets^.MainView, 'toggle-cursor-item', @Gtk2_ItemFocusChanged, AWidgetInfo);
end;
SignalConnect(PGtkWidget(Widgets^.TreeModel), 'row-changed', @Gtk2_ItemChanged, AWidgetInfo);
SignalConnect(PGtkWidget(Widgets^.TreeModel), 'row-inserted', @Gtk2_ItemInserted, AWidgetInfo);
SignalConnect(PGtkWidget(Widgets^.TreeModel), 'row-deleted', @Gtk2_ItemDeleted, AWidgetInfo);
end;
class procedure TGtk2WSCustomListView.ColumnDelete(const ALV: TCustomListView;
const AIndex: Integer);
var
Widgets: PTVWidgets;
GtkColumn: PGtkTreeViewColumn;
begin
if not WSCheckHandleAllocated(ALV, 'ColumnDelete')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
Exit;
GtkColumn := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), AIndex);
if (GtkColumn<>nil) and GTK_IS_TREE_VIEW_COLUMN(GtkColumn) then
gtk_tree_view_remove_column(PGtkTreeView(Widgets^.MainView), GtkColumn);
end;
class function TGtk2WSCustomListView.ColumnGetWidth(const ALV: TCustomListView;
const AIndex: Integer; const AColumn: TListColumn): Integer;
var
Widgets: PTVWidgets;
GtkColumn: PGtkTreeViewColumn;
begin
Result := -1;
if not WSCheckHandleAllocated(ALV, 'ColumnGetWidth')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
Exit;
GtkColumn := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), AIndex);
if GtkColumn <> nil then
begin
Result := gtk_tree_view_column_get_width(GtkColumn);
if Result = 0 then
Result := gtk_tree_view_column_get_fixed_width(GtkColumn);
end;
end;
class procedure TGtk2WSCustomListView.ColumnInsert(const ALV: TCustomListView;
const AIndex: Integer; const AColumn: TListColumn);
var
Widgets: PTVWidgets;
column: PGtkTreeViewColumn;
pixrenderer,
textrenderer: PGtkCellRenderer;
WidgetInfo: PWidgetInfo;
begin
if not WSCheckHandleAllocated(ALV, 'ColumnInsert')
then Exit;
WidgetInfo := GetWidgetInfo({%H-}PGtkWidget(ALV.Handle));
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
Exit;
column := gtk_tree_view_column_new();
gtk_widget_unset_flags(PGtkWidget(column), GTK_CAN_FOCUS);
// add renderers
pixrenderer := gtk_cell_renderer_pixbuf_new();
textrenderer := LCLIntfCellRenderer_New;//gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_start(column, pixrenderer, False);
//gtk_tree_view_column_set_attributes(column, pixrenderer,['pixbuf', RealIndex, nil]);
gtk_tree_view_column_pack_start(column, textrenderer, True);
//gtk_tree_view_column_set_attributes(column, textrenderer, ['text', 0, nil]);
gtk_tree_view_column_set_cell_data_func(column, pixrenderer, TGtkTreeCellDataFunc(@Gtk2WSLV_ListViewGetPixbufDataFuncForColumn), WidgetInfo, nil);
gtk_tree_view_column_set_cell_data_func(column, textrenderer, TGtkTreeCellDataFunc(@LCLIntfCellRenderer_CellDataFunc), WidgetInfo, nil);
//gtk_tree_view_column_set_cell_data_func(column, textrenderer, TGtkTreeCellDataFunc(@Gtk2WSLV_ListViewGetTextDataFunc), WidgetInfo, nil);
//store the TColumn in the column data for callbacks
g_object_set_data(G_OBJECT(column), PChar('TListColumn'), gpointer(AColumn));
// set callback for OnClick
SignalConnect(PGtkWidget(column), 'clicked', @Gtk2_ColumnClicked, Widgets^.WidgetInfo);
// insert column
gtk_tree_view_insert_column(GTK_TREE_VIEW(Widgets^.MainView), Column, AIndex);
//set clickable
gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
// do not set these here, it will be set by the lcl
(*
// set title
gtk_tree_view_column_set_title(column, PChar(AColumn.Caption));
//set width
gtk_tree_view_column_set_fixed_width(Column, AColumn.Width);
// set Visible
gtk_tree_view_column_set_visible(Column, AColumn.Visible);
// set MinWidth
if AColumn.MinWidth > 0 then
gtk_tree_view_column_set_min_width(Column, AColumn.MinWidth);
// set MaxWidth
if AColumn.MaxWidth > 0 then
gtk_tree_view_column_set_max_width(Column, AColumn.MaxWidth);
//set resizable
gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN (column), True);
*)
end;
class procedure TGtk2WSCustomListView.ColumnMove(const ALV: TCustomListView;
const AOldIndex, ANewIndex: Integer; const AColumn: TListColumn);
var
Widgets: PTVWidgets;
Column: PGtkTreeViewColumn;
PrevColumn: PGtkTreeViewColumn;
begin
if not WSCheckHandleAllocated(ALV, 'ColumnMove') then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
if not GTK_IS_TREE_VIEW(Widgets^.MainView) then Exit;
Column := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), AOldIndex);
if Column <> nil then
begin
if ANewIndex = 0 then
PrevColumn := nil
else
PrevColumn := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), ANewIndex);
gtk_tree_view_move_column_after(PGtkTreeView(Widgets^.MainView), Column, PrevColumn);
end;
end;
class procedure TGtk2WSCustomListView.ColumnSetAlignment(const ALV: TCustomListView;
const AIndex: Integer; const AColumn: TListColumn;
const AAlignment: TAlignment);
var
Widgets: PTVWidgets;
GtkColumn: PGtkTreeViewColumn;
Alignment: gfloat;
Value: TGValue;
renderers: PGList;
textrenderer: PGtkCellRenderer;
begin
if not WSCheckHandleAllocated(ALV, 'ColumnSetAlignment')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
Exit;
GtkColumn := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), AIndex);
if GtkColumn <> nil then
begin
renderers := gtk_tree_view_column_get_cell_renderers(GtkColumn);
textrenderer := PGtkCellRenderer(g_list_last(renderers)^.data);
g_list_free(renderers);
Alignment := AlignToGtkAlign(AAlignment);
Value.g_type := G_TYPE_FLOAT;
Value.data[0].v_float:= Alignment;
g_object_set_property(G_OBJECT(textrenderer), PChar('xalign'), @Value);
{now we call set alignment because it calls update over visible rows in col}
gtk_tree_view_column_set_alignment(GtkColumn, Alignment);
end;
end;
class procedure TGtk2WSCustomListView.ColumnSetAutoSize(const ALV: TCustomListView;
const AIndex: Integer; const AColumn: TListColumn; const AAutoSize: Boolean);
const
SizingMap: array[Boolean] of TGtkTreeViewColumnSizing = (
GTK_TREE_VIEW_COLUMN_FIXED,
GTK_TREE_VIEW_COLUMN_AUTOSIZE
);
var
Widgets: PTVWidgets;
GtkColumn: PGtkTreeViewColumn;
begin
if not WSCheckHandleAllocated(ALV, 'ColumnSetAutoSize')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
Exit;
GtkColumn := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), AIndex);
if GtkColumn <> nil then
begin
gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(GtkColumn), True);
gtk_tree_view_column_set_sizing(GtkColumn, SizingMap[AAutoSize]);
end;
end;
class procedure TGtk2WSCustomListView.ColumnSetCaption(const ALV: TCustomListView;
const AIndex: Integer; const AColumn: TListColumn; const ACaption: String);
var
Widgets: PTVWidgets;
GtkColumn: PGtkTreeViewColumn;
begin
if not WSCheckHandleAllocated(ALV, 'ColumnSetCaption')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
Exit;
GtkColumn := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), AIndex);
if GtkColumn <> nil then
gtk_tree_view_column_set_title(GtkColumn, PChar(EscapeUnderscores(ACaption)));
end;
class procedure TGtk2WSCustomListView.ColumnSetImage(const ALV: TCustomListView;
const AIndex: Integer; const AColumn: TListColumn; const AImageIndex: Integer
);
begin
if not WSCheckHandleAllocated(ALV, 'ColumnSetImage')
then Exit;
// ToDo: TGtk2WSCustomListView.ColumnSetImage
//DebugLn('TODO: Gtk2. TGtk2WSCustomListView.ColumnSetImage');
end;
class procedure TGtk2WSCustomListView.ColumnSetMaxWidth(const ALV: TCustomListView;
const AIndex: Integer; const AColumn: TListColumn; const AMaxWidth: Integer);
var
Widgets: PTVWidgets;
GtkColumn: PGtkTreeViewColumn;
begin
if not WSCheckHandleAllocated(ALV, 'ColumnSetMaxWidth')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
Exit;
GtkColumn := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), AIndex);
if GtkColumn <> nil then
gtk_tree_view_column_set_max_width(GtkColumn, AMaxWidth - Ord(AMaxWidth=0));
end;
class procedure TGtk2WSCustomListView.ColumnSetMinWidth(const ALV: TCustomListView;
const AIndex: Integer; const AColumn: TListColumn; const AMinWidth: integer);
var
Widgets: PTVWidgets;
GtkColumn: PGtkTreeViewColumn;
begin
if not WSCheckHandleAllocated(ALV, 'ColumnSetMinWidth')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
Exit;
GtkColumn := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), AIndex);
if GtkColumn <> nil then
gtk_tree_view_column_set_min_width(GtkColumn, AMinWidth - Ord(AMinWidth=0));
end;
class procedure TGtk2WSCustomListView.ColumnSetWidth(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AWidth: Integer);
var
Widgets: PTVWidgets;
GtkColumn: PGtkTreeViewColumn;
begin
if not WSCheckHandleAllocated(ALV, 'ColumnSetWidth')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
Exit;
GtkColumn := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), AIndex);
if GtkColumn <> nil then
begin
GtkColumn^.width := 0;
gtk_tree_view_column_set_fixed_width(GtkColumn, AWidth + Ord(AWidth<1));
end;
end;
class procedure TGtk2WSCustomListView.ColumnSetVisible(const ALV: TCustomListView;
const AIndex: Integer; const AColumn: TListColumn; const AVisible: Boolean);
var
Widgets: PTVWidgets;
GtkColumn: PGtkTreeViewColumn;
begin
if not WSCheckHandleAllocated(ALV, 'ColumnSetVisible')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
Exit;
with Widgets^ do
begin
GtkColumn := gtk_tree_view_get_column(PGtkTreeView(MainView), AIndex);
if AVisible then
g_object_set_data(G_OBJECT(GtkColumn), PChar('Visible'), gpointer(ptrint(1)))
else
g_object_set_data(G_OBJECT(GtkColumn), PChar('Visible'), gpointer(ptrint(0)));
if TLVHack(ALV).ViewStyle = vsReport then begin
gtk_tree_view_column_set_visible(GtkColumn, AVisible);
end;
end;
end;
class procedure TGtk2WSCustomListView.ColumnSetSortIndicator(
const ALV: TCustomListView; const AIndex: Integer;
const AColumn: TListColumn; const ASortIndicator: TSortIndicator);
const
GtkOrder : array [ TSortIndicator] of TGtkSortType = (0, GTK_SORT_ASCENDING, GTK_SORT_DESCENDING);
var
Widgets: PTVWidgets;
GtkColumn: PGtkTreeViewColumn;
begin
if not WSCheckHandleAllocated(ALV, 'ColumnSetCaption')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
Exit;
GtkColumn := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), AIndex);
if GtkColumn <> nil then
begin
if ASortIndicator = siNone then
gtk_tree_view_column_set_sort_indicator(GtkColumn, false)
else
begin
gtk_tree_view_column_set_sort_indicator(GtkColumn, true);
gtk_tree_view_column_set_sort_order(GtkColumn, GtkOrder[ASortIndicator]);
end;
end;
end;
class procedure TGtk2WSCustomListView.ItemDelete(const ALV: TCustomListView;
const AIndex: Integer);
var
Widgets: PTVWidgets;
{$IFDEF USEORIGTREEMODEL}
Iter: TGtkTreeIter;
{$ENDIF}
begin
if not WSCheckHandleAllocated(ALV, 'ItemDelete')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
with Widgets^ do begin
{$IFDEF USEORIGTREEMODEL}
if gtk_tree_model_iter_nth_child(TreeModel, @Iter, nil, AIndex) then
begin
gtk_list_store_remove(TreeModel, @Iter);
end;
{$ELSE}
PLCLListViewModel(TreeModel)^.NotifyRowDeleted(AIndex);
{$ENDIF}
end;
end;
class function TGtk2WSCustomListView.ItemDisplayRect(const ALV: TCustomListView; const AIndex, ASubItem: Integer;
ACode: TDisplayCode): TRect;
var
Widgets: PTVWidgets;
ItemRect, IconRect: TGdkRectangle;
Column: PGtkTreeViewColumn;
Path: PGtkTreePath;
X, Y, L, T, W, H: GInt;
ARect: TGdkRectangle;
R: TRect;
ANewCell: PGtkCellRenderer;
ANewPath: PGtkTreePath;
APGList: PGList;
pixrenderer: PGtkCellRenderer;
AWidth: gint;
AHeight: gint;
begin
Result := Rect(0, 0, 0, 0);
if not WSCheckHandleAllocated(ALV, 'ItemDisplayRect') then
Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
with Widgets^ do
begin
if gtk_widget_realized(MainView) = False then
exit;
Path := gtk_tree_path_new_from_indices(AIndex, -1);
try
if GTK_IS_TREE_VIEW(MainView) then
begin
Column := gtk_tree_view_get_column(PGtkTreeView(MainView), ASubItem);
gtk_tree_view_get_cell_area(PGtkTreeView(MainView), Path, Column, @ItemRect);
if gtk_tree_view_get_headers_visible(PGtkTreeView(MainView)) then
begin
gtk_tree_view_column_cell_get_size(gtk_tree_view_get_column(PGtkTreeView(MainView), 0),
@ARect, @L, @T, @W, @H);
inc(ItemRect.y, H);
end;
if (ACode in [drIcon, drLabel]) and not TLVHack(ALV).OwnerDraw then
begin
IconRect := ItemRect;
APGList := gtk_tree_view_column_get_cell_renderers(gtk_tree_view_get_column(PGtkTreeView(MainView), 0));
pixrenderer := PGtkCellRenderer(g_list_last(APGList)^.prev^.data);
gtk_cell_renderer_get_fixed_size(pixrenderer, @AWidth, @AHeight);
if AWidth > 0 then
IconRect.Width := AWidth - 2;
if AHeight > 0 then
IconRect.Height := AHeight - 2;
g_list_free(APGList);
if ACode = drIcon then
ItemRect := IconRect
else
begin
ItemRect.x += AWidth + 2;
ItemRect.y += 2; // offset
ItemRect.Width -= AWidth + 2;
ItemRect.Height -= 2; // offset
end;
end;
end
else
if GTK_IS_ICON_VIEW(MainView) then
begin
ItemRect.x := 0;
ItemRect.y := 0;
ItemRect.width := gtk_icon_view_get_item_width(PGtkIconView(MainView));
ItemRect.height := 0;
if Path <> nil then
begin
ANewPath := nil;
ANewCell := nil;
R := ALV.ClientRect;
l := 0;
t := 0;
Result := Rect(0, 0, 0, 0);
while t < R.Bottom - 1 do
begin
l := 0;
while l < R.Right - 1 do
begin
if gtk_icon_view_get_item_at_pos(PGtkIconView(MainView), l, t, ANewPath, ANewCell) then
begin
if (ANewPath <> nil) and (gtk_tree_path_compare(Path, ANewPath) = 0) then
begin
gtk_cell_renderer_get_size(ANewCell, PGtkWidget(MainView), @ItemRect, @x,@y,@w,@h);
Result := Rect(l, t, w + l, h + t);
ItemRect := GdkRectFromRect(Result);
if ANewPath <> nil then
gtk_tree_path_free(ANewPath);
break;
end;
if ANewPath <> nil then
gtk_tree_path_free(ANewPath);
end;
inc(l, 1);
end;
inc(t, 1);
if not IsRectEmpty(Result) then
break;
end;
end;
end;
finally
gtk_tree_path_free(Path);
end;
Result := RectFromGdkRect(ItemRect);
end;
end;
class procedure TGtk2WSCustomListView.ItemExchange(const ALV: TCustomListView;
AItem: TListItem; const AIndex1, AIndex2: Integer);
var
Widgets: PTVWidgets;
Path: PGtkTreePath;
ItemRect: TGdkRectangle;
begin
if not WSCheckHandleAllocated(ALV, 'ItemExchange') then
exit;
// gtk2 needs only update
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
if GetViewModel(Widgets^.MainView) = nil then
exit;
if not gtk_widget_realized(Widgets^.MainView) then
exit;
if GTK_IS_TREE_VIEW(Widgets^.MainView) then
begin
Path := gtk_tree_path_new_from_indices(AIndex1, -1);
gtk_tree_view_get_cell_area(PGtkTreeView(Widgets^.MainView), Path, nil, @ItemRect);
gtk_tree_path_free(Path);
if ItemRect.height = 0 then
begin
Path := gtk_tree_path_new_from_indices(AIndex2, -1);
gtk_tree_view_get_cell_area(PGtkTreeView(Widgets^.MainView), Path, nil, @ItemRect);
gtk_tree_path_free(Path);
end;
end else
ItemRect.height := 1; // force redraw
if ItemRect.height <> 0 then // item is visible
gtk_widget_queue_draw(Widgets^.MainView);
end;
class procedure TGtk2WSCustomListView.ItemMove(const ALV: TCustomListView;
AItem: TListItem; const AFromIndex, AToIndex: Integer);
var
Widgets: PTVWidgets;
Path: PGtkTreePath;
ItemRect: TGdkRectangle;
begin
if not WSCheckHandleAllocated(ALV, 'ItemMove') then
exit;
// gtk2 needs only update
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
if GetViewModel(Widgets^.MainView) = nil then
exit;
if not gtk_widget_realized(Widgets^.MainView) then
exit;
if GTK_IS_TREE_VIEW(Widgets^.MainView) then
begin
Path := gtk_tree_path_new_from_indices(AFromIndex, -1);
gtk_tree_view_get_cell_area(PGtkTreeView(Widgets^.MainView), Path, nil, @ItemRect);
gtk_tree_path_free(Path);
if ItemRect.height = 0 then
begin
Path := gtk_tree_path_new_from_indices(AToIndex, -1);
gtk_tree_view_get_cell_area(PGtkTreeView(Widgets^.MainView), Path, nil, @ItemRect);
gtk_tree_path_free(Path);
end;
end else
ItemRect.height := 1; // force redraw
if ItemRect.height <> 0 then // item is visible
gtk_widget_queue_draw(Widgets^.MainView);
end;
class function TGtk2WSCustomListView.ItemGetChecked(const ALV: TCustomListView;
const AIndex: Integer; const AItem: TListItem): Boolean;
begin
Result := TLVItemHack(AItem).GetCheckedInternal;
end;
class function TGtk2WSCustomListView.ItemGetState(const ALV: TCustomListView;
const AIndex: Integer; const AItem: TListItem; const AState: TListItemState;
out AIsSet: Boolean): Boolean;
var
Widgets: PTVWidgets;
Path: PGtkTreePath;
pstr: PChar;
Column: PGtkTreeViewColumn;
Cell: PGtkCellRenderer;
begin
Result := False;
if not WSCheckHandleAllocated(ALV, 'ItemGetState')
then Exit;
AIsSet := False;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
with Widgets^ do
begin
if GetViewModel(MainView) = nil then
Exit; // we are in the midst of a begin update end update pair and the following will fail and cause gtk debug messages
case AState of
lisCut,
lisDropTarget:
begin
//TODO: do something with the rowcolor ?
end;
lisFocused:
begin
if GTK_IS_TREE_VIEW(MainView) then begin
Path:=nil;
Column:=nil;
gtk_tree_view_get_cursor(PGtkTreeView(MainView), Path, Column)
end
else
if GTK_IS_ICON_VIEW(MainView) then
gtk_icon_view_get_cursor(PGtkIconView(MainView), Path, Cell{%H-})
else
Path := nil;
if (Path<>nil) then
begin
pstr := gtk_tree_path_to_string(path);
AIsSet:=(StrToInt(pstr) = AIndex);
g_free(pstr);
end else
AIsSet:=false;
gtk_tree_path_free(Path);
Result := True;
end;
lisSelected:
begin
Path := gtk_tree_path_new_from_string(PChar(IntToStr(AIndex)));
if GTK_IS_TREE_VIEW(MainView) then
AIsSet := gtk_tree_selection_path_is_selected(TreeSelection, Path)
else
if GTK_IS_ICON_VIEW(MainView) then
AIsSet := gtk_icon_view_path_is_selected(PGtkIconView(MainView), Path)
else
AIsSet := False;
gtk_tree_path_free(Path);
Result := True;
end;
end;
end;
end;
class procedure TGtk2WSCustomListView.ItemInsert(const ALV: TCustomListView;
const AIndex: Integer; const AItem: TListItem);
var
Widgets: PTVWidgets;
{$IFDEF USEORIGTREEMODEL}
Iter: TGtkTreeIter;
Index: Integer;
{$ENDIF}
begin
if not WSCheckHandleAllocated(ALV, 'ItemInsert')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
with Widgets^ do
begin
{$IFDEF USEORIGTREEMODEL}
if AIndex = -1 then
Index := gtk_tree_model_iter_n_children(TreeModel, nil)
else
Index := AIndex;
gtk_list_store_insert_with_values(PGtkListStore(TreeModel), @Iter, Index, 0, Pointer(AItem), -1);
{$ELSE}
if not (lisfWSItemsCreated in ALV.Items.Flags) then
Exit;
PLCLListViewModel(TreeModel)^.NotifyRowInserted(AIndex);
{$ENDIF}
end;
end;
class procedure TGtk2WSCustomListView.ItemSetChecked(
const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem;
const AChecked: Boolean);
begin
if not WSCheckHandleAllocated(ALV, 'ItemSetChecked')
then Exit;
// nothing needed here
end;
class procedure TGtk2WSCustomListView.ItemSetImage(const ALV: TCustomListView;
const AIndex: Integer; const AItem: TListItem; const ASubIndex,
AImageIndex: Integer);
var
Widgets: PTVWidgets;
Path: PGtkTreePath;
ItemRect: TGdkRectangle;
BitImage: TBitmap;
pixbuf: PGDKPixBuf;
i, ImgListWidth: Integer;
ImgList: TCustomImageList;
ImgListRes: TCustomImageListResolution;
begin
if not WSCheckHandleAllocated(ALV, 'ItemSetImage')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
with Widgets^ do
begin
if not gtk_widget_realized(MainView) then
begin
// DebugLn('WARNING: TGtk2WSCustomListView.ItemSetImage: MainView is not realized.');
Exit;
end;
Path := gtk_tree_path_new_from_indices(AIndex, -1);
if GTK_IS_TREE_VIEW(MainView) then
gtk_tree_view_get_cell_area(PGtkTreeView(MainView), Path, nil, @ItemRect)
else
ItemRect.height := 1; // force redraw
gtk_tree_path_free(Path);
if ItemRect.height <> 0 then // item is visible
begin
if (TListView(ALV).ViewStyle in [vsSmallIcon, vsReport, vsList]) then
begin
ImgList := TListView(ALV).SmallImages;
ImgListWidth := TListView(ALV).SmallImagesWidth;
end
else
if (TListView(ALV).ViewStyle = vsIcon) then
begin
ImgList := TListView(ALV).LargeImages;
ImgListWidth := TListView(ALV).LargeImagesWidth;
end;
if Assigned(ImgList) and (ImgList.Count > 0) and (AImageIndex >= 0) then
begin
ImgListRes := ImgList.ResolutionForPPI[ImgListWidth, ALV.Font.PixelsPerInch, ALV.GetCanvasScaleFactor].Resolution;
if (ImgList.Count <> Widgets^.Images.Count) then
begin
if (TListView(ALV).ViewStyle in [vsSmallIcon, vsReport, vsList]) then
SetImageList(ALV, lvilSmall, ImgListRes)
else
SetImageList(ALV, lvilLarge, ImgListRes);
exit;
end;
if (Widgets^.Images <> nil) then
begin
for i := 0 to Widgets^.Images.Count-1 do
if i = AImageIndex then
gdk_pixbuf_unref(PGdkPixBuf(Widgets^.Images.Items[i]));
pixbuf := nil;
BitImage := TBitmap.Create;
try
ImgListRes.GetBitmap(AImageIndex, BitImage);
Gtk2_PixBufFromBitmap(BitImage,pixbuf);
Widgets^.Images.Items[AImageIndex] := pixbuf;
if GTK_IS_TREE_VIEW(MainView) then
gtk_tree_view_column_queue_resize(gtk_tree_view_get_column(PGtkTreeView(MainView), ASubIndex));
finally
BitImage.Free;
end;
end;
end;
gtk_widget_queue_draw(MainView);
end;
end;
end;
class procedure TGtk2WSCustomListView.ItemSetState(const ALV: TCustomListView;
const AIndex: Integer; const AItem: TListItem; const AState: TListItemState;
const AIsSet: Boolean);
var
Widgets: PTVWidgets;
Path: PGtkTreePath;
BroadcastMsg: Boolean;
begin
if not WSCheckHandleAllocated(ALV, 'ItemSetState')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
// wwiInvalidEvent flag save us from infinite loop !
// when this flag is included TreeSelection 'changed' won't
// trigger - and it shouldn't LCL setted up selection.
// fixes #16399
Include(Widgets^.WidgetInfo^.Flags, wwiInvalidEvent);
try
BroadcastMsg := False;
with Widgets^ do
begin
if GetViewModel(MainView) = nil then
Exit; // we are in the midst of a begin update end update pair and the following will fail and cause gtk debug messages
case AState of
lisCut,
lisDropTarget:
begin
//TODO: do something with the rowcolor ?
end;
lisFocused:
begin
//gtk2 iter has no focus??
Path := gtk_tree_path_new_from_string(PChar(IntToStr(AIndex)));
if GTK_IS_TREE_VIEW(MainView) then
gtk_tree_view_set_cursor(PGtkTreeView(MainView), Path, nil, False)
else
if GTK_IS_ICON_VIEW(MainView) then
gtk_icon_view_set_cursor(PGtkIconView(MainView), Path, nil, False);
gtk_tree_path_free(Path);
end;
lisSelected:
begin
Path := gtk_tree_path_new_from_string(PChar(IntToStr(AIndex)));
if GTK_IS_TREE_VIEW(MainView) then
begin
if AIsSet and not gtk_tree_selection_path_is_selected(TreeSelection, Path) then
begin
gtk_tree_selection_select_path(TreeSelection, Path);
BroadcastMsg := True;
end else
if not AIsSet and gtk_tree_selection_path_is_selected(TreeSelection, Path) then
begin
gtk_tree_selection_unselect_path(TreeSelection, Path);
BroadcastMsg := True;
end;
end
else
if GTK_IS_ICON_VIEW(MainView) then
begin
if AIsSet and not gtk_icon_view_path_is_selected(PGtkIconView(MainView), Path) then
begin
gtk_icon_view_select_path(PGtkIconView(MainView), Path);
BroadCastMsg := True;
end else
if not AIsSet and gtk_icon_view_path_is_selected(PGtkIconView(MainView), Path) then
begin
gtk_icon_view_unselect_path(PGtkIconView(MainView), Path);
BroadCastMsg := True;
end;
end;
gtk_tree_path_free(Path);
if BroadcastMsg then
BroadCastListSelection(ALV, {%H-}PtrUInt(MainView), AIndex, not AIsSet);
end;
end;
end;
finally
Exclude(Widgets^.WidgetInfo^.Flags, wwiInvalidEvent);
end;
end;
class procedure TGtk2WSCustomListView.ItemSetText(const ALV: TCustomListView;
const AIndex: Integer; const AItem: TListItem; const ASubIndex: Integer;
const AText: String);
var
Widgets: PTVWidgets;
Path: PGtkTreePath;
ItemRect: TGdkRectangle;
begin
// ToDo: TGtk2WSCustomListView.ItemSetText: this function queues a draw. Is this correct?
if not WSCheckHandleAllocated(ALV, 'ItemSetText')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
with Widgets^ do
begin
if not gtk_widget_realized(MainView) then
Exit;
if GTK_IS_TREE_VIEW(MainView) then
begin
Path := gtk_tree_path_new_from_indices(AIndex, -1);
gtk_tree_view_get_cell_area(PGtkTreeView(MainView), Path, nil, @ItemRect);
gtk_tree_path_free(Path);
end
else
ItemRect.height := 1; // force redraw
if ItemRect.height <> 0 then // item is visible
gtk_widget_queue_draw(MainView);
end;
end;
class procedure TGtk2WSCustomListView.ItemShow(const ALV: TCustomListView;
const AIndex: Integer; const AItem: TListItem; const PartialOK: Boolean);
var
Widgets: PTVWidgets;
Path: PGtkTreePath;
begin
if not WSCheckHandleAllocated(ALV, 'ItemShow')
then Exit;
// TODO: TGtk2WSCustomListView.ItemShow check for partial visiblity. currently scrolls to the Item to make it fully visible
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
with Widgets^ do
begin
Path := gtk_tree_path_new_from_indices(AIndex, -1);
if GTK_IS_TREE_VIEW(MainView) then
gtk_tree_view_scroll_to_cell(PGtkTreeView(MainView), Path, nil, False, 0, 0)
else
if GTK_IS_ICON_VIEW(MainView) then
gtk_icon_view_scroll_to_path(PGtkIconView(MainView), Path, False, 0, 0);
gtk_tree_path_free(Path);
end;
end;
class function TGtk2WSCustomListView.ItemGetPosition(const ALV: TCustomListView; const AIndex: Integer): TPoint;
var
Widgets: PTVWidgets;
Path: PGtkTreePath;
ARect: TGdkRectangle;
Column: PGtkTreeViewColumn;
begin
if not WSCheckHandleAllocated(ALV, 'ItemGetPosition')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
Path := gtk_tree_path_new_from_indices(AIndex, -1);
with Widgets^ do
begin
if GTK_IS_TREE_VIEW(MainView) then
begin
Column := gtk_tree_view_get_column(PGtkTreeView(MainView), 0);
gtk_tree_view_get_cell_area(PGtkTreeView(MainView), Path, Column, @ARect);
Result.X := ARect.x;
Result.Y := Arect.y;
end
else
if GTK_IS_ICON_VIEW(MainView) then
begin
// todo: gtk gives no way to get item rectangle, while internally it uses it
Result.X := 0;
Result.Y := 0;
end;
end;
gtk_tree_path_free(Path);
end;
class procedure TGtk2WSCustomListView.ItemUpdate(const ALV: TCustomListView;
const AIndex: Integer; const AItem: TListItem);
{$IFDEF USEORIGTREEMODEL}
var
Widgets: PTVWidgets;
Iter: TGtkTreeIter;
{$ENDIF}
begin
if not WSCheckHandleAllocated(ALV, 'ItemUpdate')
then Exit;
{$IFDEF USEORIGTREEMODEL}
GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets);
with Widgets^ do
if gtk_tree_model_iter_nth_child(TreeModel, @Iter, nil, AIndex) then
gtk_list_store_set(PGtkListStore(TreeModel), @Iter, [0, Pointer(AItem), -1]);
{$ENDIF}
end;
class function TGtk2WSCustomListView.CreateHandle(const AWinControl: TWinControl;
const AParams: TCreateParams): HWND;
var
Widgets: PTVWidgets;
OrigScrollingData: PBaseScrollingWinControlData;
//ListViewData: PCustomListViewData;
//Allocation: TGTKAllocation;
ScrollWidget: PGtkScrolledWindow;
{$IFDEF USEORIGTREEMODEL}
PtrType: GType;
{$ENDIF}
SS: TPoint;
begin
Result := TGtk2WSBaseScrollingWinControl.CreateHandle(AWinControl, AParams);
if Result = 0 then Exit;
ScrollWidget := {%H-}PGtkScrolledWindow(Result);
gtk_widget_unset_flags(ScrollWidget^.hscrollbar, GTK_CAN_FOCUS);
gtk_widget_unset_flags(ScrollWidget^.vscrollbar, GTK_CAN_FOCUS);
SS := Gtk2TranslateScrollStyle(TListView(AWinControl).ScrollBars);
gtk_scrolled_window_set_policy(ScrollWidget,SS.X, SS.Y);
gtk_scrolled_window_set_shadow_type(ScrollWidget,
BorderStyleShadowMap[TCustomListView(AWinControl).BorderStyle]);
gtk_widget_show(PGtkWidget(ScrollWidget));
Widgets := nil;
New(Widgets);
with Widgets^ do
begin
Images := nil;
OldTreeSelection := nil;
{$IFDEF USEORIGTREEMODEL}
PtrType := G_TYPE_POINTER;
TreeModel := gtk_list_store_newv(1, @PtrType);
{$ELSE}
TreeModel:= LCLListViewModelNew(TCustomListView(AWinControl));
{$ENDIF}
if TLVHack(AWinControl).ViewStyle in [vsIcon,vsSmallIcon] then
begin
MainView := gtk_icon_view_new_with_model(TreeModel);
TreeSelection := nil;
if TLVHack(AWinControl).IconOptions.AutoArrange then
begin
//MaxM: if we have only Horizontal bar active then set columns to Max
if TLVHack(AWinControl).Scrollbars in [ssHorizontal, ssAutoHorizontal] then
gtk_icon_view_set_columns(PGtkIconView(MainView), MaxInt)
else gtk_icon_view_set_columns(PGtkIconView(MainView), -1);
end
else begin
//We mimic the behavior of Windows although we should take scrollbars into account
if TLVHack(AWinControl).IconOptions.Arrangement = iaTop then
gtk_icon_view_set_columns(PGtkIconView(MainView), -1)
else gtk_icon_view_set_columns(PGtkIconView(MainView), MaxInt);
end;
end
else
begin
if IsOldGtk2 then
OldTreeSelection := g_list_alloc;
MainView := gtk_tree_view_new_with_model(TreeModel);
TreeSelection := PGtkTreeSelection(gtk_tree_view_get_selection(PGtkTreeView(MainView)));
end;
g_object_unref(G_OBJECT(TreeModel));
// we added +1 because Ord(vsIcon) returns 0, so it's nil ptr
g_object_set_data(PGObject(MainView),'lcllistviewstyle', {%H-}gpointer(PtrInt(Ord(TLVHack(AWinControl).ViewStyle) + 1)));
gtk_container_add(GTK_CONTAINER(ScrollWidget),PGtkWidget(MainView));
// create widget info
// already created in TGtkWSBaseScrollingWinControl
// Replace the ScrollingInfo with our info
WidgetInfo := GetWidgetInfo(ScrollWidget);
OrigScrollingData := WidgetInfo^.UserData;
ScrollingData := OrigScrollingData^;
ItemCacheCount := 0;
WidgetInfo^.UserData := Widgets;
Dispose(OrigScrollingData);
WidgetInfo^.CoreWidget := PGtkWidget(MainView);
g_object_set_data(Pointer(MainView), 'widgetinfo', WidgetInfo);
gtk_widget_show_all(PGtkWidget(MainView));
if not AWinControl.HandleObjectShouldBeVisible and not (csDesigning in AWinControl.ComponentState) then
gtk_widget_hide(PGtkWidget(ScrollWidget));
SetListCallbacks(PGtkWidget(ScrollWidget), Widgets, WidgetInfo);
end;
end;
class procedure TGtk2WSCustomListView.DestroyHandle(const AWinControl: TWinControl);
var
Widgets: PTVWidgets;
i: Integer;
begin
GetCommonTreeViewWidgets({%H-}PGtkWidget(AWinControl.Handle), Widgets);
// on widget destroy we have no ItemDeleted notification and we must destroy ItemCache ourself
// if things will change please remove this destroy
Widgets^.ItemCacheCount := 0;
SetLength(Widgets^.ItemCache, 0);
if Widgets^.OldTreeSelection <> nil then
begin
g_list_free(Widgets^.OldTreeSelection);
Widgets^.OldTreeSelection := nil;
end;
if Widgets^.Images <> nil then
begin
for i := 0 to Widgets^.Images.Count-1 do
if Widgets^.Images.Items[i] <> nil then
gdk_pixbuf_unref(PGDKPixBuf(Widgets^.Images.Items[i]));
FreeAndNil(Widgets^.Images);
end;
if Widgets^.MainView <> nil then
g_object_set_data(Pointer(Widgets^.MainView), 'widgetinfo', nil);
TGtk2WSWinControl.DestroyHandle(AWinControl);
end;
class procedure TGtk2WSCustomListView.BeginUpdate(const ALV: TCustomListView);
begin
if not WSCheckHandleAllocated(ALV, 'BeginUpdate') then
exit;
g_object_set_data({%H-}PGObject(ALV.Handle),'lcl_gtkwidget_in_update', ALV);
end;
class procedure TGtk2WSCustomListView.EndUpdate(const ALV: TCustomListView);
begin
if not WSCheckHandleAllocated(ALV, 'EndUpdate') then
exit;
g_object_set_data({%H-}PGObject(ALV.Handle),'lcl_gtkwidget_in_update', nil);
end;
class function TGtk2WSCustomListView.GetBoundingRect(const ALV: TCustomListView): TRect;
begin
Result:=Rect(0,0,0,0);
if not WSCheckHandleAllocated(ALV, 'GetBoundingRect')
then Exit;
//DebugLn('TODO: TGtk2WSCustomListView.GetBoundingRect');
end;
class function TGtk2WSCustomListView.GetDropTarget(const ALV: TCustomListView): Integer;
var
Widgets: PTVWidgets;
begin
// TODO: implement
Result := -1;
if not WSCheckHandleAllocated(ALV, 'GetDropTarget')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
end;
class function TGtk2WSCustomListView.GetFocused(const ALV: TCustomListView): Integer;
var
Widgets: PTVWidgets;
Path: PGtkTreePath;
Column: PGtkTreeViewColumn;
Cell: PGtkCellRenderer;
begin
Result := -1;
if not WSCheckHandleAllocated(ALV, 'GetFocused')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
with Widgets^ do
begin
if GTK_IS_TREE_VIEW(MainView) then begin
Path:=nil;
Column:=nil;
gtk_tree_view_get_cursor(PGtkTreeView(MainView), Path, Column);
end
else begin
if GTK_IS_ICON_VIEW(MainView) then begin
Cell:=nil;
gtk_icon_view_get_cursor(PGtkIconView(MainView), Path, Cell);
end
else
Path := nil;
end;
if Path <> nil then
begin
Result := StrToInt(PChar(Path));
gtk_tree_path_free(Path);
end;
end;
end;
class function TGtk2WSCustomListView.GetHoverTime(const ALV: TCustomListView): Integer;
var
Widgets: PTVWidgets;
begin
// TODO: implement
Result := -1; // = default
if not WSCheckHandleAllocated(ALV, 'GetHoverTime')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
with Widgets^ do
begin
end;
end;
class function TGtk2WSCustomListView.GetItemAt(const ALV: TCustomListView; x, y: integer): Integer;
var
Widgets: PTVWidgets;
ItemPath: PGtkTreePath;
Column: PGtkTreeViewColumn;
cx, cy: gint;
begin
Result := -1;
if not WSCheckHandleAllocated(ALV, 'GetItemAt')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
if GTK_IS_TREE_VIEW(Widgets^.MainView) then
begin
// gtk2 >= 2.19 changed treeview api
if gtk_minor_version >= 19 then
begin
gdk_window_get_position(gtk_tree_view_get_bin_window(PGtkTreeView(Widgets^.MainView)), @cx, @cy);
Dec(x, cx);
Dec(y, cy);
end else
begin
// convert X, Y to bin window coords
x := x + Round(PGtkTreeView(Widgets^.MainView)^.priv^.hadjustment^.value);
if GTK_TREE_VIEW_FLAG_SET(PGtkTreeView(Widgets^.MainView), GTK_TREE_VIEW_HEADERS_VISIBLE) then
begin
gdk_window_get_size(PGtkTreeView(Widgets^.MainView)^.priv^.header_window, @cx, @cy);
y := y - cy;
end;
end;
ItemPath:=nil;
Column:=nil;
if gtk_tree_view_get_path_at_pos(PGtkTreeView(Widgets^.MainView), x, y, ItemPath, Column, nil, nil) then
begin
if ItemPath <> nil then
begin
Result := gtk_tree_path_get_indices(ItemPath)^;
gtk_tree_path_free(ItemPath);
end;
end;
end
else
if GTK_IS_ICON_VIEW(Widgets^.MainView) then
begin
ItemPath := gtk_icon_view_get_path_at_pos(PGtkIconView(Widgets^.MainView),
x + Widgets^.ScrollingData.HValue, y + Widgets^.ScrollingData.VValue);
if ItemPath <> nil then
begin
Result := gtk_tree_path_get_indices(ItemPath)^;
gtk_tree_path_free(ItemPath);
end;
end;
end;
class function TGtk2WSCustomListView.GetSelCount(const ALV: TCustomListView): Integer;
var
Widgets: PTVWidgets;
AList: PGList;
begin
Result := 0;
if not WSCheckHandleAllocated(ALV, 'GetSelCount')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
with Widgets^ do
begin
if GTK_IS_TREE_VIEW(MainView) then
AList := gtk_tree_selection_get_selected_rows(TreeSelection, nil)
else
if GTK_IS_ICON_VIEW(MainView) then
AList := gtk_icon_view_get_selected_items(PGtkIconView(MainView))
else
Exit;
if AList <> nil then
begin
Result := g_list_length(AList);
g_list_free(AList);
end;
end;
end;
class function TGtk2WSCustomListView.GetSelection(const ALV: TCustomListView): Integer;
var
Widgets: PTVWidgets;
Iter: TGtkTreeIter;
Path: PGtkTreePath;
AList: PGList;
begin
Result := -1;
if not WSCheckHandleAllocated(ALV, 'GetSelection')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
with Widgets^ do
begin
if GTK_IS_TREE_VIEW(MainView) then
gtk_tree_selection_get_selected(TreeSelection, nil, @Iter)
else
if GTK_IS_ICON_VIEW(MainView) then
begin
AList := gtk_icon_view_get_selected_items(PGtkIconView(MainView));
if AList <> nil then
begin
Path := g_list_first(AList)^.data;
g_list_free(AList);
end else
Path := nil;
end;
Path := gtk_tree_model_get_path(TreeModel, @Iter);
Result := StrToInt(PChar(Path));
gtk_tree_path_free(Path);
end;
end;
class function TGtk2WSCustomListView.GetTopItem(const ALV: TCustomListView): Integer;
var
Res: Boolean;
s, e: PGtkTreePath;
Widgets: PTVWidgets;
Num: Pgint;
begin
Result := -1;
if not WSCheckHandleAllocated(ALV, 'GetTopItem') then
exit;
Res := false;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
with Widgets^ do
begin
if GTK_IS_TREE_VIEW(MainView) then
Res := gtk_tree_view_get_visible_range(PGtkTreeView(Widgets^.MainView), s, e)
else
if GTK_IS_ICON_VIEW(MainView) then
Res := gtk_icon_view_get_visible_range(PGtkTreeView(Widgets^.MainView), s, e)
else
Exit;
end;
if Res then
begin
Num := gtk_tree_path_get_indices(s);
if Num <> nil then Result := Num^;
gtk_tree_path_free(s);
gtk_tree_path_free(e);
end;
end;
class function TGtk2WSCustomListView.GetViewOrigin(const ALV: TCustomListView): TPoint;
var
Widgets: PTVWidgets;
begin
if not WSCheckHandleAllocated(ALV, 'GetViewOrigin')
then begin
Result := Point(0, 0);
Exit;
end;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
// not really needed to retrieve the adjustments, since the TVWidgets has this info also based on events
Result := Point(Widgets^.ScrollingData.HValue, Widgets^.ScrollingData.VValue);
end;
class function TGtk2WSCustomListView.GetVisibleRowCount(const ALV: TCustomListView): Integer;
var
Res: Boolean;
s, e: PGtkTreePath;
Widgets: PTVWidgets;
Num1,Num2: Pgint;
begin
Result := -1;
if not WSCheckHandleAllocated(ALV, 'GetVisibleRowCount') then
exit;
Result := 0;
Res := false;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
with Widgets^ do
begin
if GTK_IS_TREE_VIEW(MainView) then
Res := gtk_tree_view_get_visible_range(PGtkTreeView(Widgets^.MainView), s, e)
else
if GTK_IS_ICON_VIEW(MainView) then
Res := gtk_icon_view_get_visible_range(PGtkTreeView(Widgets^.MainView), s, e)
else
Exit;
end;
if Res then
begin
Num1 := gtk_tree_path_get_indices(s);
Num2 := gtk_tree_path_get_indices(e);
if (Num1 <> nil) and (Num2 <> nil) then Result := Num2^-Num1^+1;
gtk_tree_path_free(s);
gtk_tree_path_free(e);
end;
end;
class procedure TGtk2WSCustomListView.SelectAll(const ALV: TCustomListView;
const AIsSet: Boolean);
var
Widgets: PTVWidgets;
begin
if not WSCheckHandleAllocated(ALV, 'SelectAll') then
exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
Include(Widgets^.WidgetInfo^.Flags, wwiInvalidEvent);
try
with Widgets^ do
begin
if GTK_IS_TREE_VIEW(MainView) then
begin
if AIsSet then
gtk_tree_selection_select_all(gtk_tree_view_get_selection(PGtkTreeView(MainView)))
else
gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(PGtkTreeView(MainView)));
end else
begin
if GTK_IS_ICON_VIEW(MainView) then
begin
if AIsSet then
gtk_icon_view_select_all(PGtkIconView(MainView))
else
gtk_icon_view_unselect_all(PGtkIconView(MainView));
end;
end;
end;
finally
Exclude(Widgets^.WidgetInfo^.Flags, wwiInvalidEvent);
end;
end;
class procedure TGtk2WSCustomListView.SetAllocBy(const ALV: TCustomListView;
const AValue: Integer);
begin
if not WSCheckHandleAllocated(ALV, 'SetAllocBy')
then Exit;
end;
class procedure TGtk2WSCustomListView.SetColor(const AWinControl: TWinControl);
var
AWidget: PGTKWidget;
begin
if not WSCheckHandleAllocated(AWinControl, 'SetColor') then
Exit;
AWidget := {%H-}PGtkWidget(AWinControl.Handle);
AWidget := GetOrCreateWidgetInfo(AWidget)^.CoreWidget;
Gtk2WidgetSet.SetWidgetColor(AWidget,
AWinControl.Font.Color,
AWinControl.Color,
[GTK_STATE_NORMAL, GTK_STATE_ACTIVE, GTK_STATE_PRELIGHT, GTK_STYLE_BASE]);
end;
class procedure TGtk2WSCustomListView.SetDefaultItemHeight(
const ALV: TCustomListView; const AValue: Integer);
begin
if not WSCheckHandleAllocated(ALV, 'SetDefaultItemHeight')
then Exit;
end;
class procedure TGtk2WSCustomListView.SetFont(const AWinControl: TWinControl;
const AFont: TFont);
var
Widget: PGtkWidget;
begin
if not WSCheckHandleAllocated(AWinControl, 'SetFont') then
Exit;
Widget := {%H-}PGtkWidget(AWinControl.Handle);
Widget := GetOrCreateWidgetInfo(Widget)^.CoreWidget;
Gtk2WidgetSet.SetWidgetFont(Widget, AFont);
Gtk2WidgetSet.SetWidgetColor(Widget, AFont.Color, clNone,
[GTK_STATE_NORMAL,GTK_STATE_ACTIVE,
GTK_STATE_PRELIGHT,GTK_STATE_SELECTED,
GTK_STYLE_TEXT]);
end;
class procedure TGtk2WSCustomListView.SetHotTrackStyles(const ALV: TCustomListView;
const AValue: TListHotTrackStyles);
begin
if not WSCheckHandleAllocated(ALV, 'SetHotTrackStyles')
then Exit;
end;
class procedure TGtk2WSCustomListView.SetImageList(const ALV: TCustomListView;
const AList: TListViewImageList; const AValue: TCustomImageListResolution);
var
Widgets: PTVWidgets;
BitImage: TBitmap;
pixbuf: PGDKPixBuf;
i: Integer;
APGList: PGList;
pixrenderer: PGtkCellRenderer;
begin
if not WSCheckHandleAllocated(ALV, 'SetImageList')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
gtk_widget_queue_draw(Widgets^.MainView);
if ((AList = lvilLarge) and (TLVHack(ALV).ViewStyle = vsIcon)) or
((AList = lvilSmall) and (TLVHack(ALV).ViewStyle <> vsIcon)) then
begin
if Widgets^.Images <> nil then
begin
for i := 0 to Widgets^.Images.Count-1 do
gdk_pixbuf_unref(PGdkPixBuf(Widgets^.Images.Items[i]));
Widgets^.Images.Clear;
end;
if AValue = nil then
Exit;
if Widgets^.Images = nil then
Widgets^.Images := TList.Create;
if (AValue.Count = 0) and GTK_IS_TREE_VIEW(Widgets^.MainView) and (TLVHack(ALV).Columns.Count > 0) and
not TLVHack(ALV).OwnerDraw then
begin
APGList := gtk_tree_view_column_get_cell_renderers(gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), 0));
pixrenderer := PGtkCellRenderer(g_list_last(APGList)^.prev^.data);
gtk_cell_renderer_set_fixed_size(pixrenderer, AValue.Width + 2, AValue.Height + 2);
g_list_free(APGList);
gtk_tree_view_column_queue_resize(gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), 0));
end;
for i := 0 to AValue.Count-1 do
begin
pixbuf := nil;
BitImage := TBitmap.Create;
try
AValue.GetBitmap(i, BitImage);
Gtk2_PixBufFromBitmap(BitImage, pixbuf);
if GTK_IS_TREE_VIEW(Widgets^.MainView) and (TLVHack(ALV).Columns.Count > 0) and
not TLVHack(ALV).OwnerDraw then
begin
APGList := gtk_tree_view_column_get_cell_renderers(gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), 0));
pixrenderer := PGtkCellRenderer(g_list_last(APGList)^.prev^.data);
gtk_cell_renderer_set_fixed_size(pixrenderer, AValue.Width + 2, AValue.Height + 2);
g_list_free(APGList);
gtk_tree_view_column_queue_resize(gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), 0));
end;
Widgets^.Images.Add(pixbuf);
finally
BitImage.Free;
end;
end;
end;
end;
class procedure TGtk2WSCustomListView.SetItemsCount(const ALV: TCustomListView;
const Avalue: Integer);
var
Widgets: PTVWidgets;
{$IFDEF USEORIGTREEMODEL}
Iter: TGtkTreeIter;
Index: Integer;
{$ENDIF}
begin
if not WSCheckHandleAllocated(ALV, 'SetItemsCount')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
with Widgets^ do
begin
{$IFNDEF USEORIGTREEMODEL}
g_object_ref(TreeModel);
gtk_tree_view_set_model(PGtkTreeView(MainView), nil);
gtk_tree_view_set_model(PGtkTreeView(MainView), TreeModel);
g_object_unref(TreeModel);
{$ELSE}
gtk_list_store_clear(PGtkListStore(TreeModel));
for Index := 0 to AValue - 1 do
gtk_list_store_insert_with_values(PGtkListStore(TreeModel), @Iter, Index, 0, nil, -1);
{$ENDIF}
end;
end;
class procedure TGtk2WSCustomListView.SetProperty(const ALV: TCustomListView;
const AProp: TListViewProperty; const AIsSet: Boolean);
var
Widgets: PTVWidgets;
begin
if not WSCheckHandleAllocated(ALV, 'SetProperty')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
SetPropertyInternal(ALV, Widgets, AProp, AIsSet);
end;
class procedure TGtk2WSCustomListView.SetProperties(const ALV: TCustomListView;
const AProps: TListViewProperties);
var
Widgets: PTVWidgets;
Prop: TListViewProperty;
begin
if not WSCheckHandleAllocated(ALV, 'SetProperties')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
for Prop := Low(Prop) to High(Prop) do
SetPropertyInternal(ALV, Widgets, Prop, Prop in AProps);
end;
class procedure TGtk2WSCustomListView.SetScrollBars(const ALV: TCustomListView;
const AValue: TScrollStyle);
var
SS:TPoint;
ScrollWidget: PGtkScrolledWindow;
begin
if not WSCheckHandleAllocated(ALV, 'SetScrollBars') then
exit;
ScrollWidget := {%H-}PGtkScrolledWindow(ALV.Handle);
SS := Gtk2TranslateScrollStyle(AValue);
gtk_scrolled_window_set_policy(ScrollWidget ,SS.X, SS.Y);
end;
class procedure TGtk2WSCustomListView.SetSort(const ALV: TCustomListView;
const AType: TSortType; const AColumn: Integer;
const ASortDirection: TSortDirection);
var
Widgets: PTVWidgets;
begin
if not WSCheckHandleAllocated(ALV, 'SetSort')
then Exit;
// gtk2 needs only update
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets);
if GetViewModel(Widgets^.MainView) = nil then
exit;
if not gtk_widget_realized(Widgets^.MainView) then
exit;
if GTK_IS_TREE_VIEW(Widgets^.MainView) then
gtk_widget_queue_draw(Widgets^.MainView);
end;
class procedure TGtk2WSCustomListView.SetViewOrigin(const ALV: TCustomListView;
const AValue: TPoint);
var
Widgets: PTVWidgets;
begin
if not WSCheckHandleAllocated(ALV, 'SetViewOrigin')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets{%H-});
//DebugLn(['TGtk2WSCustomListView.SetViewOrigin ',GetWidgetDebugReport(Widgets^.MainView)]);
if not GTK_WIDGET_REALIZED(Widgets^.MainView) then exit;
if GTK_IS_TREE_VIEW(Widgets^.MainView) then
gtk_tree_view_scroll_to_point(PGtkTreeView(Widgets^.MainView), AValue.X, AValue.Y)
else
if GTK_IS_ICON_VIEW(Widgets^.MainView) then
begin
// TODO: iconview
end;
end;
class procedure TGtk2WSCustomListView.SetViewStyle(const ALV: TCustomListView;
const AValue: TViewStyle);
var
APtrIntData: PtrInt;
procedure ShowColumns(const Widgets: PTVWidgets; const Show: Boolean);
var
List: PGList;
GtkColumn: PGtkTreeViewColumn;
i: Integer;
begin
if not GTK_IS_TREE_VIEW(Widgets^.MainView) then
Exit;
List := gtk_tree_view_get_columns(PGtkTreeView(Widgets^.MainView));
for i := 0 to g_list_length(List) - 1 do
begin
GtkColumn := g_list_nth_data(List, i);
if GtkColumn = nil then
Continue;
if not Show or
(Show and ({%H-}PtrUInt(g_object_get_data(G_OBJECT(GtkColumn),
PChar('Visible'))) <> 0)) then
gtk_tree_view_column_set_visible(GtkColumn, Show);
end;
g_list_free(List)
end;
var
Widgets: PTVWidgets;
begin
if not WSCheckHandleAllocated(ALV, 'SetViewStyle')
then Exit;
GetCommonTreeViewWidgets({%H-}PGtkWidget(ALV.Handle), Widgets{%H-});
if g_object_get_data(PGObject(Widgets^.MainView),'lcllistviewstyle') <> nil then
APtrIntData := {%H-}PtrInt(g_object_get_data(PGObject(Widgets^.MainView),'lcllistviewstyle'))
else
APtrIntData := -1;
if (APtrIntData <> -1) and (APtrIntData - 1 <> Ord(AValue)) then
begin
// we have to free the GtkTreeView and Create GtkIconView etc depending on the new style
//RecreateMainView(ALV);
// we actually need to recreate our ListView since not only the widget changes but also columns
RecreateWnd(ALV);
Exit;
end;
ShowColumns(Widgets, AValue = vsReport);
with Widgets^ do
begin
case AValue of
vsIcon,
vsSmallIcon:
begin
SetNeedDefaultColumn(ALV, True);
end;
vsList:
begin
SetNeedDefaultColumn(ALV, True);
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW (MainView), False);
end;
vsReport:
begin
SetNeedDefaultColumn(ALV, False);
if TLVHack(ALV).ShowColumnHeaders = True then
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW (MainView), True);
end;
end;
end;
// inherited SetViewStyle(ALV, Avalue);
end;