{%MainUnit gtk2wscomctrls.pp} { $Id$ ***************************************************************************** * * * This file is part of the Lazarus Component Library (LCL) * * * * See the file COPYING.modifiedLGPL, included in this distribution, * * for details about the copyright. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * * ***************************************************************************** } type TLVHack = class(TCustomListView) end; //////////////////////////////////////// //// Event Code ///////////////////// //////////////////////////////////////// procedure Gtk2_ItemFocusChanged(treeview: PGtkTreeView; WidgetInfo: PWidgetInfo);cdecl; var msg: TLMNotify; NM: TNMListView; path: PGtkTreePath; column: PGtkTreeViewColumn; begin //DebugLn('Gtk2_ItemFocusChanged'); // the defocus of the oldrow isn't send gtk_tree_view_get_cursor(treeview, path, column); if path = nil then exit; gtk_tree_path_free(path); msg.Msg := CN_NOTIFY; FillChar(NM, SizeOf(NM), 0); NM.hdr.hwndfrom := PtrInt(WidgetInfo^.CoreWidget); NM.hdr.code := LVN_ITEMCHANGED; NM.iItem := StrToInt(gtk_tree_path_to_string(path)); NM.iSubItem := 0; NM.uNewState := LVIS_FOCUSED; NM.uChanged := LVIF_STATE; msg.NMHdr := @NM.hdr; DeliverMessage(WidgetInfo^.LCLObject, msg); end; procedure Gtk2_ItemDeleted(model: PGtkTreeModel; path: PGtkTreePath; WidgetInfo: PWidgetInfo); cdecl; var msg: TLMNotify; NM: TNMListView; ALV: TLVHack; Widgets: PTVWidgets; begin //DebugLn('Gtk2_ItemDeleted'); msg.Msg := CN_NOTIFY; FillChar(NM, SizeOf(NM), 0); NM.hdr.hwndfrom := PtrInt(WidgetInfo^.CoreWidget); NM.hdr.code := LVN_DELETEITEM; NM.iItem := StrToInt(gtk_tree_path_to_string(path)); msg.NMHdr := @NM.hdr; DeliverMessage(WidgetInfo^.LCLObject, msg); ALV := TLVHack(WidgetInfo^.LCLObject); if ALV.Items.Count = 0 then begin Widgets := PTVWidgets(WidgetInfo^.UserData); Widgets^.ItemCache.Free; Widgets^.ItemCache := nil; end; end; procedure Gtk2_ItemInserted(model: PGtkTreeModel; path: PGtkTreePAth; Iter: PGtkTreeIter; WidgetInfo: PWidgetInfo); cdecl; var msg: TLMNotify; NM: TNMListView; begin //DebugLn('Gtk2_ItemInserted'); msg.Msg := CN_NOTIFY; FillChar(NM, SizeOf(NM), 0); NM.hdr.hwndfrom := PtrInt(WidgetInfo^.CoreWidget); NM.hdr.code := LVN_INSERTITEM; NM.iItem := StrToInt(gtk_tree_path_to_string(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(model: PGtkTreeModel; path: PGtkTreePAth; Iter: PGtkTreeIter; 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, SizeOf(NM), 0); NM.hdr.hwndfrom := PtrInt(WidgetInfo^.CoreWidget); NM.hdr.code := LVN_COLUMNCLICK; NM.iItem := -1; NM.iSubItem := AColumn.Index; msg.NMHdr := @NM.hdr; DeliverMessage(WidgetInfo^.LCLObject, msg); end; procedure Gtk2_ItemSelectionChanged(selection: PGtkTreeSelection; WidgetInfo: PWidgetInfo); cdecl; var msg: TLMNotify; NM: TNMListView; Widgets: PTVWidgets; AIndex: String; i: Integer; begin //DebugLn('Gtk2_ItemSelectionChanged'); Widgets := PTVWidgets(WidgetInfo^.UserData); msg.Msg := CN_NOTIFY; if (widgets=nil) or (Widgets^.ItemCache=nil) or (Widgets^.ItemCache.Count=0) then begin debugln(' Gtk2_ItemSelectionChanged ItemChache=nil ',tComponent(widgetInfo^.lclObject).name); exit; end; for i := 0 to Widgets^.ItemCache.Count -1 do begin FillChar(NM, SizeOf(NM), 0); NM.hdr.hwndfrom := PtrInt(Widgets^.MainView); NM.hdr.code := LVN_ITEMCHANGED; AIndex := Widgets^.ItemCache.Strings[i]; NM.iItem := StrToInt(AIndex); NM.iSubItem := 0;//AColumn; if Widgets^.ItemCache.Objects[i] <> nil then NM.uOldState := LVIS_SELECTED else NM.uNewState := LVIS_SELECTED; NM.uChanged := LVIF_STATE; msg.NMHdr := @NM.hdr; DeliverMessage(WidgetInfo^.LCLObject, msg); end; Widgets^.ItemCache.Clear; end; function Gtk2WSLV_ItemSelected(selection: PGtkTreeSelection; model: PGtkTreeModel; path: PGtkTreePath; path_is_currently_selected: GBoolean; WidgetInfo: PWidgetInfo): GBoolean; cdecl; var Widgets: PTVWidgets; i: Integer; Str: String; msg: TLMNotify; NM: TNMListView; 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); Str := gtk_tree_path_to_string(path); FillChar(NM, SizeOf(NM), 0); NM.hdr.hwndfrom := PtrInt(WidgetInfo^.CoreWidget); NM.hdr.code := LVN_ITEMCHANGING; NM.iItem := StrToInt(Str); NM.iSubItem := 0;//AColumn; NM.uNewState := LVIS_SELECTED; NM.uChanged := LVIF_STATE; msg.NMHdr := @NM.hdr; {Result := }DeliverMessage(WidgetInfo^.LCLObject, msg){ = 0}; if not Result then Exit; // this stops a loop when you use the shift key to select multiple entries for i := 0 to Widgets^.ItemCache.Count-1 do begin if (Widgets^.ItemCache.Strings[i] = Str) and (Widgets^.ItemCache.Objects[i] = TObject(ptrint(Ord(path_is_currently_selected)))) then begin Result := False; Exit; end; end; Widgets^.ItemCache.AddObject(Str,TObject(ptrint(Ord(path_is_currently_selected)))); end; { TGtk2WSCustomListView } class function TGtk2WSCustomListView.IsIconView(const ALV: TCustomListView): Boolean; begin Result := False; if TLVHack(ALV).ViewStyle in [vsIcon{, vsTile?}] then Result := True; end; class procedure TGtk2WSCustomListView.ReCreateListStore(const ALV: TCustomListView; const TVWidgets: PTVWidgets); var GTypeArray: PGType; NewListStore: PGtkListStore; nColumns: Integer; i: Integer; begin nColumns := (TLVHack(ALV).Columns.Count*2); if nColumns = 0 then nColumns := 2; // Add One Column By Default GetMem(GTypeArray, SizeOf(GType)*(nColumns+1)); for i := 0 to (nColumns div 2)-1 do begin GTypeArray[i*2] := GDK_TYPE_PIXBUF; GTypeArray[(i*2)+1] := G_TYPE_STRING; end; GTypeArray[nColumns] := 0; NewListStore := gtk_list_store_newv(nColumns, GTypeArray); ReAllocMem(GTypeArray, 0); gtk_tree_view_set_model(PGtkTreeView(TVWidgets^.MainView), PGtkTreeModel(NewListStore)); TVWidgets^.TreeModel := NewListStore; end; class procedure TGtk2WSCustomListView.ReCreateItems(const ALV: TCustomListView); var Widgets: PTVWidgets; i,x: Integer; Item: TListItem; begin GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets); gtk_list_store_clear(PGtkListStore(Widgets^.TreeModel)); for i := 0 to TLVHack(ALV).Items.Count-1 do begin Item := TLVHack(ALV).Items.Item[i]; ItemInsert(ALV, i , Item); ItemSetText(ALV, i, Item, 0, Item.Caption); for X := 0 to Min(Item.SubItems.Count-1, TLVHack(ALV).Columns.Count-2) do begin ItemSetText(ALV, i, Item, x+1, Item.SubItems.Strings[x]); end; end; end; class procedure TGtk2WSCustomListView.SetPropertyInternal(const ALV: TCustomListView; const Widgets: PTVWidgets; const AProp: TListViewProperty; const AIsSet: Boolean); begin with Widgets^ do begin case AProp of lvpAutoArrange: begin // TODO: implement ?? end; lvpCheckboxes: begin // TODO: implement 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 gtk_tree_view_set_rules_hint(PGtkTreeView(MainView), AIsSet); 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 AIsSet then gtk_tree_selection_set_mode(TreeSelection, GTK_SELECTION_MULTIPLE) else gtk_tree_selection_set_mode(TreeSelection, GTK_SELECTION_SINGLE); end; lvpOwnerDraw: begin // TODO: implement // use custom images/widgets ? 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.SetCallbacks(const AScrollWidget: PGtkWidget; const Widgets: PTVWidgets; const AWidgetInfo: PWidgetInfo); begin TGtkWSBaseScrollingWinControl.SetCallbacks(AScrollWidget, AWidgetInfo); TGtkWSWinControl.SetCallbacks(PGtkObject(Widgets^.MainView), TComponent(AWidgetInfo^.LCLObject)); // the callbacks for OnColumnClick are set when the column is created in ColumnInsert 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^.TreeModel), 'row-changed', @Gtk2_ItemChanged, AWidgetInfo); SignalConnect(PGtkWidget(Widgets^.TreeModel), 'row-inserted', @Gtk2_ItemInserted, AWidgetInfo); SignalConnect(PGtkWidget(Widgets^.TreeModel), 'row-deleted', @Gtk2_ItemDeleted, AWidgetInfo); SignalConnect(PGtkWidget(Widgets^.MainView), 'toggle-cursor-row', @Gtk2_ItemFocusChanged, 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(PGtkWidget(ALV.Handle), Widgets); ReCreateListStore(ALV, PTVWidgets(Widgets^.WidgetInfo^.UserData)); ReCreateItems(ALV); with Widgets^ do begin GtkColumn := gtk_tree_view_get_column(PGtkTreeView(MainView), AIndex); gtk_tree_view_remove_column(PGtkTreeView(MainView), GtkColumn); end; end; class function TGtk2WSCustomListView.ColumnGetWidth(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn): Integer; var Widgets: PTVWidgets; GtkColumn: PGtkTreeViewColumn; i: Integer; begin Result := -1; if not WSCheckHandleAllocated(ALV, 'ColumnGetWidth') then Exit; GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets); with Widgets^ do begin i := AColumn.Index; GtkColumn := gtk_tree_view_get_column(PGtkTreeView(MainView), i); Result := gtk_tree_view_column_get_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; RealIndex: Integer; begin if not WSCheckHandleAllocated(ALV, 'ColumnInsert') then Exit; if AIndex < 0 then RealIndex := 0; RealIndex := AIndex *2; GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets); if gtk_tree_model_get_n_columns(Widgets^.TreeModel) div 2 < TLVHack(ALV).Columns.Count then begin ReCreateListStore(ALV, PTVWidgets(Widgets^.WidgetInfo^.UserData)); ReCreateItems(ALV); GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets); end; column := gtk_tree_view_column_new(); gtk_widget_unset_flags(PGtkWidget(column), GTK_CAN_FOCUS); // add renderers pixrenderer := gtk_cell_renderer_pixbuf_new(); textrenderer := 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',RealIndex+ 1, 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); // dont 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(PGtkWidget(ALV.Handle), Widgets); with Widgets^ do begin Column := gtk_tree_view_get_column(PGtkTreeView(MainView), AOldIndex); if ANewIndex = 0 then PrevColumn := nil else PrevColumn := gtk_tree_view_get_column(PGtkTreeView(MainView), ANewIndex-1); gtk_tree_view_move_column_after(PGtkTreeView(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; begin if not WSCheckHandleAllocated(ALV, 'ColumnSetAlignment') then Exit; GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets); with Widgets^ do begin GtkColumn := gtk_tree_view_get_column(PGtkTreeView(MainView), AIndex); gtk_tree_view_column_set_alignment(GtkColumn, AlignToGtkAlign(AAlignment)); end; //DebugLn('ColSetALignment AIndex=',IntTOStr(AIndex),' (GtkColumn=nil)=', BoolToStr(GtkColumn=nil)); end; class procedure TGtk2WSCustomListView.ColumnSetAutoSize(const ALV: TCustomListView; const AIndex: Integer; const AColumn: TListColumn; const AAutoSize: Boolean); var Widgets: PTVWidgets; GtkColumn: PGtkTreeViewColumn; ColSizing: TGtkTreeViewColumnSizing; begin if not WSCheckHandleAllocated(ALV, 'ColumnSetAutoSize') then Exit; GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets); with Widgets^ do begin GtkColumn := gtk_tree_view_get_column(PGtkTreeView(MainView), AIndex); if AAutoSize then ColSizing := GTK_TREE_VIEW_COLUMN_AUTOSIZE else ColSizing := GTK_TREE_VIEW_COLUMN_FIXED; gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN (GtkColumn), True); gtk_tree_view_column_set_sizing(GtkColumn, ColSizing); 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(PGtkWidget(ALV.Handle), Widgets); with Widgets^ do begin GtkColumn := gtk_tree_view_get_column(PGtkTreeView(MainView), AIndex); gtk_tree_view_column_set_title(GtkColumn, PChar(ACaption)); end; 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; //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(PGtkWidget(ALV.Handle), Widgets); with Widgets^ do begin GtkColumn := gtk_tree_view_get_column(PGtkTreeView(MainView), AIndex); gtk_tree_view_column_set_max_width(GtkColumn, AMaxWidth - Ord(AMaxWidth=0)); end; 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(PGtkWidget(ALV.Handle), Widgets); with Widgets^ do begin GtkColumn := gtk_tree_view_get_column(PGtkTreeView(MainView), AIndex); gtk_tree_view_column_set_min_width(GtkColumn, AMinWidth - Ord(AMinWidth=0)); end; 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(PGtkWidget(ALV.Handle), Widgets); with Widgets^ do begin GtkColumn := gtk_tree_view_get_column(PGtkTreeView(MainView), AIndex); 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(PGtkWidget(ALV.Handle), Widgets); 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.ItemDelete(const ALV: TCustomListView; const AIndex: Integer); var Widgets: PTVWidgets; Iter: TGtkTreeIter; begin if not WSCheckHandleAllocated(ALV, 'ItemDelete') then Exit; GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets); with Widgets^ do begin if gtk_tree_model_iter_nth_child(TreeModel, @Iter, nil, AIndex) then begin gtk_list_store_remove(TreeModel, @Iter); end; end; end; class function TGtk2WSCustomListView.ItemGetState(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const AState: TListItemState; out AIsSet: Boolean): Boolean; var Widgets: PTVWidgets; Iter: TGtkTreeIter; begin Result := False; if not WSCheckHandleAllocated(ALV, 'ItemGetState') then Exit; AIsSet := False; GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets); with Widgets^ do begin if gtk_tree_model_iter_nth_child(TreeModel, @Iter, nil, AIndex) then begin case AState of lisCut, lisDropTarget: begin //TODO: do something with the rowcolor ? end; lisFocused: begin AIsSet := gtk_tree_selection_iter_is_selected(TreeSelection, @Iter);//gtk2 iter has no focus?? Result := True; end; lisSelected: begin AIsSet := gtk_tree_selection_iter_is_selected(TreeSelection, @Iter); Result := True; end; end; end; end; end; class procedure TGtk2WSCustomListView.ItemInsert(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem); var Widgets: PTVWidgets; Iter: TGtkTreeIter; // BitImage: TBitmap; begin if not WSCheckHandleAllocated(ALV, 'ItemInsert') then Exit; GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets); with Widgets^ do begin if ItemCache = nil then begin ItemCache := TStringList.Create; end; if AIndex = -1 then gtk_list_store_append(PGtkListStore(TreeModel), @Iter) else gtk_list_store_insert(PGtkListStore(TreeModel), @Iter, AIndex); { //Icon if (TLVHack(ALV).SmallImages <> nil) and (AItem.ImageIndex > -1) then begin BitImage := TBitmap.Create; TLVHack(ALV).SmallImages.GetBitmap(AItem.ImageIndex,BitImage); gtk_list_store_set(TreeModel, @Iter, [0 ,PGdkPixmap(PGDIObject(BitImage.handle)^.GDIBitmapObject), -1]); end; } end; end; class procedure TGtk2WSCustomListView.ItemSetImage(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const ASubIndex, AImageIndex: Integer); var Widgets: PTVWidgets; Iter: TGtkTreeIter; BitImage: TBitmap; GPixBuf: PGDKPixBuf; begin if not WSCheckHandleAllocated(ALV, 'ItemSetImage') then Exit; GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets); with Widgets^ do begin //Icon if gtk_tree_model_iter_nth_child(TreeModel, @Iter, nil, AIndex) then begin if (TLVHack(ALV).SmallImages <> nil) and (AItem.ImageIndex > -1) then begin BitImage := TBitmap.Create; TLVHack(ALV).SmallImages.GetBitmap(AItem.ImageIndex,BitImage); GPixBuf:=gdk_pixbuf_get_from_drawable(nil, PGdkDrawable(PGdkPixmap(PGDIObject(BitImage.handle)^.GDIBitmapObject)), nil,0,0,0,0,-1,-1) ; end else begin gpixbuf:=nil; end; gtk_list_store_set(TreeModel, @Iter, [0 ,gpixbuf, -1]); 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; Iter: TGtkTreeIter; Path: PGtkTreePath; begin if not WSCheckHandleAllocated(ALV, 'ItemSetState') then Exit; GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets); with Widgets^ do begin if gtk_tree_model_iter_nth_child(TreeModel, @Iter, nil, AIndex) then begin 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 AIsSet then begin gtk_tree_view_set_cursor(PGtkTreeView(MainView), Path, nil, False); end else begin gtk_tree_view_set_cursor(PGtkTreeView(MainView), nil, nil, False); end; gtk_tree_path_free(Path); end; lisSelected: begin if AIsSet then begin if not gtk_tree_selection_iter_is_selected(TreeSelection, @Iter) then gtk_tree_selection_select_iter(TreeSelection, @Iter); end else begin if gtk_tree_selection_iter_is_selected(TreeSelection, @Iter) then gtk_tree_selection_unselect_iter(TreeSelection, @Iter); end; end; end; end; end; end; class procedure TGtk2WSCustomListView.ItemSetText(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const ASubIndex: Integer; const AText: String); var Widgets: PTVWidgets; Iter: TGtkTreeIter; Str: String; begin if not WSCheckHandleAllocated(ALV, 'ItemSetText') then Exit; GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets); with Widgets^ do begin gtk_tree_model_iter_nth_child(TreeModel, @Iter, nil, AIndex); if ASubIndex = 0 then Str := AItem.Caption else begin if AItem.Subitems.Count < ASubIndex then Exit;// Str := AItem.Subitems.Strings[ASubIndex-1]; end; gtk_list_store_set(PGtkListStore(TreeModel), @Iter, [(ASubIndex*2)+1, PChar(Str), -1]); end; end; class procedure TGtk2WSCustomListView.ItemShow(const ALV: TCustomListView; const AIndex: Integer; const AItem: TListItem; const PartialOK: Boolean); var Widgets: PTVWidgets; Path: PGtkTreePath; StrIndex: String; begin if not WSCheckHandleAllocated(ALV, 'ItemShow') then Exit; //TODO check for partial visiblity. currently scrolls to the Item to make it fully visible StrIndex := IntToStr(AItem.Index); GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets); with Widgets^ do begin Path := gtk_tree_path_new_from_string(PChar(StrIndex)); gtk_tree_view_scroll_to_cell(PGtkTreeView(MainView),Path,nil,false,0,0); gtk_tree_path_free(Path); end; end; class function TGtk2WSCustomListView.CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): HWND; var Widgets: PTVWidgets; ListView: TCustomListView; OrigScrollingData: PBaseScrollingWinControlData; //ListViewData: PCustomListViewData; //Allocation: TGTKAllocation; ScrollWidget: PGtkScrolledWindow; nColumns: Integer; GTypeArray: PGType; i: Integer; begin ListView := TCustomListView(AWinControl as TCustomListView); Result := TGtkWSBaseScrollingWinControl.CreateHandle(AWinControl, AParams); if Result = 0 then Exit; ScrollWidget := PGtkScrolledWindow(Result); gtk_widget_unset_flags(ScrollWidget^.hscrollbar, GTK_CAN_FOCUS); gtk_widget_unset_flags(ScrollWidget^.vscrollbar, GTK_CAN_FOCUS); gtk_scrolled_window_set_policy(ScrollWidget, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_widget_show(PGtkWidget(ScrollWidget)); nColumns := (TLVHack(ListView).Columns.Count*2){+1}; if nColumns = 0 then nColumns := 2; // Add One Column By Default Widgets := nil; New(Widgets); with Widgets^ do begin ItemCache := nil; GetMem(GTypeArray, SizeOf(GType)*(nColumns{+1})); for i := 0 to (nColumns div 2) -1 do begin GTypeArray[i*2] := GDK_TYPE_PIXBUF; GTypeArray[(i*2)+1] := G_TYPE_STRING; end; GTypeArray[nColumns] := 0; // -1; TreeModel := gtk_list_store_newv(nColumns, GTypeArray); ReAllocMem(GTypeArray, 0); MainView:=gtk_tree_view_new_with_model(TreeModel); //MainView:=gtk_tree_view_new; g_object_unref (G_OBJECT (TreeModel)); TreeSelection := PGtkTreeSelection(gtk_tree_view_get_selection(PGtkTreeView(MainView))); 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; Widgets^.ScrollingData := OrigScrollingData^; WidgetInfo^.UserData := Widgets; Dispose(OrigScrollingData); WidgetInfo^.CoreWidget := PGtkWidget(MainView); //SetMainWidget(ScrollWidget, MainView); //GetWidgetInfo(ScrollWidget, True)^.CoreWidget := PGtkWidget(MainView); gtk_widget_show_all(PGtkWidget(MainView)); SetCallbacks(PGtkWidget(ScrollWidget), Widgets, WidgetInfo); // SetCallbacks(PGtkWidget(MainView), Widgets^, WidgetInfo); end; (* New(Widgets); Widgets^.WidgetInfo := GetWidgetInfo(ScrollWidget); SetCallbacks(PGtkWidget(ScrollWidget), Widgets^, Widgets^.WidgetInfo); *) end; class procedure TGtk2WSCustomListView.BeginUpdate(const ALV: TCustomListView); var Widgets: PTVWidgets; begin if not WSCheckHandleAllocated(ALV, 'BeginUpdate') then Exit; GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets); g_object_ref(Widgets^.TreeModel); gtk_tree_view_set_model(PGtkTreeView(Widgets^.MainView), nil); end; class procedure TGtk2WSCustomListView.EndUpdate(const ALV: TCustomListView); var Widgets: PTVWidgets; begin if not WSCheckHandleAllocated(ALV, 'EndUpdate') then Exit; GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets); gtk_tree_view_set_model(PGtkTreeView(Widgets^.MainView), Widgets^.TreeModel); g_object_unref(Widgets^.TreeModel); 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(PGtkWidget(ALV.Handle), Widgets); end; class function TGtk2WSCustomListView.GetFocused(const ALV: TCustomListView): Integer; var Widgets: PTVWidgets; Path: PGtkTreePath; Column: PGtkTreeViewColumn; begin Result := -1; if not WSCheckHandleAllocated(ALV, 'GetFocused') then Exit; GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets); with Widgets^ do begin gtk_tree_view_get_cursor(PGtkTreeView(MainView), Path, Column); if Path <> nil then Result := StrToInt(PChar(Path)); 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; //DebugLn('TODO: TGtk2WSCustomListView.GetHoverTime'); GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets); with Widgets^ do begin 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(PGtkWidget(ALV.Handle), Widgets); with Widgets^ do begin AList := gtk_tree_selection_get_selected_rows(TreeSelection, nil); Result := g_list_length(AList); g_list_free(AList); end; end; class function TGtk2WSCustomListView.GetSelection(const ALV: TCustomListView ): Integer; var Widgets: PTVWidgets; Iter: TGtkTreeIter; Path: PGtkTreePath; begin Result := -1; if not WSCheckHandleAllocated(ALV, 'GetSelection') then Exit; GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets); with Widgets^ do begin gtk_tree_selection_get_selected(TreeSelection, nil, @Iter); 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; begin Result := -1; if not WSCheckHandleAllocated(ALV, 'GetTopItem') then Exit; 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(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; begin Result := -1; if not WSCheckHandleAllocated(ALV, 'GetVisibleRowCount') then Exit; end; class procedure TGtk2WSCustomListView.SetAllocBy(const ALV: TCustomListView; const AValue: Integer); begin if not WSCheckHandleAllocated(ALV, 'SetAllocBy') then Exit; end; class procedure TGtk2WSCustomListView.SetDefaultItemHeight( const ALV: TCustomListView; const AValue: Integer); begin if not WSCheckHandleAllocated(ALV, 'SetDefaultItemHeight') then Exit; end; class procedure TGtk2WSCustomListView.SetHotTrackStyles(const ALV: TCustomListView; const AValue: TListHotTrackStyles); begin if not WSCheckHandleAllocated(ALV, 'SetHotTrackStyles') then Exit; end; class procedure TGtk2WSCustomListView.SetHoverTime(const ALV: TCustomListView; const AValue: Integer); begin if not WSCheckHandleAllocated(ALV, 'SetHoverTime') then Exit; end; class procedure TGtk2WSCustomListView.SetImageList(const ALV: TCustomListView; const AList: TListViewImageList; const AValue: TCustomImageList); begin if not WSCheckHandleAllocated(ALV, 'SetImageList') then Exit; 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(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(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 ScrollWidget: PGtkScrolledWindow; hPolicy, vPolicy: TGtkPolicyType; begin if not WSCheckHandleAllocated(ALV, 'SetScrollBars') then Exit; ScrollWidget := PGtkScrolledWindow(ALV.Handle); case AValue of ssHorizontal, ssBoth: hPolicy := GTK_POLICY_ALWAYS; ssAutoHorizontal, ssAutoBoth: hPolicy := GTK_POLICY_AUTOMATIC; else hPolicy := GTK_POLICY_NEVER; end; case AValue of ssVertical, ssBoth: vPolicy := GTK_POLICY_ALWAYS; ssAutoVertical, ssAutoBoth: vPolicy := GTK_POLICY_AUTOMATIC; else vPolicy := GTK_POLICY_NEVER; end; gtk_scrolled_window_set_policy(ScrollWidget, hPolicy, vPolicy); end; class procedure TGtk2WSCustomListView.SetSort(const ALV: TCustomListView; const AType: TSortType; const AColumn: Integer); begin if not WSCheckHandleAllocated(ALV, 'SetSort') then Exit; // inherited SetSort(ALV, AType, AColumn); end; class procedure TGtk2WSCustomListView.SetViewOrigin(const ALV: TCustomListView; const AValue: TPoint); var Widgets: PTVWidgets; begin if not WSCheckHandleAllocated(ALV, 'SetViewOrigin') then Exit; GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets); gtk_tree_view_scroll_to_point(PGtkTreeView(Widgets^.MainView), AValue.X, AValue.Y); end; class procedure TGtk2WSCustomListView.SetViewStyle(const ALV: TCustomListView; const Avalue: TViewStyle); procedure ShowColumns(const Widgets: PTVWidgets; const Show: Boolean); var GtkColumn: PGtkTreeViewColumn; i: Integer; begin for i := 1 to TLVHack(ALV).Columns.Count-1 do begin GtkColumn := gtk_tree_view_get_column(PGtkTreeView(Widgets^.MainView), i); if not (Show) or (Show and (PtrInt(g_object_get_data(G_OBJECT(GtkColumn), PChar('Visible')))<>0)) then gtk_tree_view_column_set_visible(GtkColumn, Show); end; end; var Widgets: PTVWidgets; begin if not WSCheckHandleAllocated(ALV, 'SetViewStyle') then Exit; GetCommonTreeViewWidgets(PGtkWidget(ALV.Handle), Widgets); ShowColumns(Widgets, AValue = vsReport); with Widgets^ do begin case AValue of vsList: begin gtk_tree_view_set_headers_visible(GTK_TREE_VIEW (MainView), False); end; vsReport: begin if TLVHack(ALV).ShowColumnHeaders = True then gtk_tree_view_set_headers_visible(GTK_TREE_VIEW (MainView), True); end; end; end; // inherited SetViewStyle(ALV, Avalue); // this one is going to be fun because you have to free the GtkTreeView and Create GtkIconView etc depending on the new style end; {procedure TGtk2WSCustomListView.UpdateProperties( const ACustomListView: TCustomListView); var Widgets: TTVWidgets; GtkColumn: PGtkTreeViewColumn; Column: TListColumn; Iter: TGtkTreeIter; Item: TListItem; X, Y: Integer; Count: Integer; BitImage: TBitmap; begin if Not(ACustomListView.HandleAllocated) then exit; GetCommonTreeViewWidgets(PGtkWidget(ACustomListView.Handle), Widgets); with Widgets^ do begin // set up columns.. for X := 0 to ACustomListView.Columns.Count-1 do begin; GtkColumn := gtk_tree_view_get_column(TreeView, X); Column := ACustomListView.Columns.Items[X]; // set captions gtk_tree_view_column_set_title(GtkColumn, PChar(Column.Caption)); // set column alignment gtk_tree_view_column_set_alignment(GtkColumn, AlignToGtkAlign(Column.Alignment)); // set auto sizing case Column.AutoSize of // The gtk2 docs say that GTK_TREE_VIEW_COLUMN_AUTOSIZE is inefficient // for large views, so perhaps this should be // GTK_TREE_VIEW_COLUMN_GROW_ONLY True : gtk_tree_view_column_set_sizing(GtkColumn, GTK_TREE_VIEW_COLUMN_AUTOSIZE); //True : gtk_tree_view_column_set_sizing(GtkColumn, GTK_TREE_VIEW_COLUMN_GROW_ONLY); False: gtk_tree_view_column_set_sizing(GtkColumn, GTK_TREE_VIEW_COLUMN_FIXED); end; // set width gtk_tree_view_column_set_fixed_width(GtkColumn, Column.Width+Ord(Column.Width=0)); // set Visible gtk_tree_view_column_set_visible(GtkColumn, Column.Visible); // set MinWidth gtk_tree_view_column_set_min_width(GtkColumn, Column.MinWidth-Ord(Column.MinWidth=0)); // set MaxWidth gtk_tree_view_column_set_max_width(GtkColumn, Column.MaxWidth-Ord(Column.MaxWidth=0)); end; // ViewStyle case ACustomListView.ViewStyle of vsReport: gtk_tree_view_set_headers_visible(TreeView, True); vsList: _set_headers_visible(TreeView, False); end; //sorting //TODO //multiselect case ACustomListView.MultiSelect of True : gtk_tree_selection_set_mode(TreeSelection, GTK_SELECTION_MULTIPLE); False: gtk_tree_selection_set_mode(TreeSelection, GTK_SELECTION_SINGLE); //GTK_SELECTION_NONE, //GTK_SELECTION_SINGLE, //GTK_SELECTION_BROWSE, //GTK_SELECTION_MULTIPLE end; //do items... for X := 0 to ACustomListView.Items.Count-1 do begin Item:= ACustomListView.Items.Item[X]; if X = 0 then gtk_tree_model_get_iter_first(TreeModel, @Iter) else gtk_tree_model_iter_next(TreeModel, @Iter); //do image if one is assigned.... if (ACustomListView.SmallImages <> nil) and (Item.ImageIndex > -1) then begin BitImage := TBitmap.Create; ACustomListView.SmallImages.GetBitmap(Item.ImageIndex,BitImage); // this doesnt seem to be working :( gtk_list_store_set(TreeModel, @Iter, [0 ,PGdkPixmap(PGDIObject(BitImage.handle)^.GDIBitmapObject), -1]); end; //Item.Caption gtk_list_store_set(TreeModel, @Iter, [1, PChar(Item.Caption), -1]); Count := ACustomListView.Columns.Count; //Item.Subitems for Y := 2 to Count-1 do begin if (Y-2) >= Item.SubItems.Count then Break; gtk_list_store_set(TreeModel, @Iter, [Y, PChar(Item.SubItems.Strings[Y-2]), -1]); end; end; end; end;}