mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-30 22:23:41 +02:00
479 lines
17 KiB
ObjectPascal
479 lines
17 KiB
ObjectPascal
{ $Id: gtk2wsstdctrls.pp 9520 2006-06-28 21:26:52Z mattias $}
|
|
{
|
|
*****************************************************************************
|
|
* Gtk2CellRenderer.pas *
|
|
* -------------------- *
|
|
* *
|
|
* *
|
|
*****************************************************************************
|
|
|
|
*****************************************************************************
|
|
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.
|
|
*****************************************************************************
|
|
|
|
An extended gtk_cell_renderer, to provide hooks for the LCL.
|
|
For example for custom drawing.
|
|
|
|
}
|
|
unit Gtk2CellRenderer;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
// RTL
|
|
Classes, SysUtils,
|
|
gtk2, gdk2, glib2,
|
|
// LCL
|
|
LCLType, LCLProc, Controls, StdCtrls, ComCtrls, LMessages,
|
|
Gtk2Int, Gtk2Proc, Gtk2Def, Gtk2Extra;
|
|
|
|
type
|
|
PLCLIntfCellRenderer = ^TLCLIntfCellRenderer;
|
|
TLCLIntfCellRenderer = record
|
|
// ! the TextRenderer must be the first attribute of this record !
|
|
TextRenderer: TGtkCellRendererText;
|
|
Index: integer;
|
|
ColumnIndex: Integer; // for TListView
|
|
end;
|
|
|
|
PLCLIntfCellRendererClass = ^TLCLIntfCellRendererClass;
|
|
TLCLIntfCellRendererClass = record
|
|
ParentClass: TGtkCellRendererTextClass;
|
|
DefaultGtkGetSize: procedure(cell: PGtkCellRenderer;
|
|
widget: PGtkWidget;
|
|
cell_area: PGdkRectangle;
|
|
x_offset: pgint;
|
|
y_offset: pgint;
|
|
width: pgint;
|
|
height: pgint); cdecl;
|
|
DefaultGtkRender: procedure(cell: PGtkCellRenderer;
|
|
window: PGdkWindow;
|
|
widget: PGtkWidget;
|
|
background_area: PGdkRectangle;
|
|
cell_area: PGdkRectangle;
|
|
expose_area:PGdkRectangle;
|
|
flags: TGtkCellRendererState); cdecl;
|
|
end;
|
|
|
|
function LCLIntfCellRenderer_GetType: TGtkType;
|
|
function LCLIntfCellRenderer_New: PGtkCellRenderer;
|
|
procedure LCLIntfCellRenderer_CellDataFunc(cell_layout:PGtkCellLayout;
|
|
cell: PGtkCellRenderer;
|
|
tree_model: PGtkTreeModel;
|
|
iter: PGtkTreeIter;
|
|
data: gpointer); cdecl;
|
|
|
|
implementation
|
|
|
|
type
|
|
TCustomListViewAccess = class(TCustomListView);
|
|
|
|
function GetControl(cell: PGtkCellRenderer; Widget: PGtkWidget): TWinControl;
|
|
var
|
|
WidgetInfo: PWidgetInfo;
|
|
LCLObject: TObject;
|
|
MainWidget: PGtkWidget;
|
|
begin
|
|
Result := nil;
|
|
MainWidget := GetMainWidget(Widget);
|
|
if MainWidget = nil then
|
|
exit;
|
|
WidgetInfo := GetWidgetInfo(MainWidget);
|
|
if WidgetInfo = nil then
|
|
WidgetInfo := GetWidgetInfo(cell);
|
|
if WidgetInfo = nil then
|
|
exit;
|
|
LCLObject := WidgetInfo^.LCLObject; // the listbox or combobox
|
|
Result := LCLObject as TWinControl;
|
|
end;
|
|
|
|
function GetItemIndex(cell: PLCLIntfCellRenderer; {%H-}widget: PGtkWidget): Integer;
|
|
begin
|
|
Result:=cell^.Index;
|
|
end;
|
|
|
|
procedure LCLIntfCellRenderer_GetSize(cell: PGtkCellRenderer; widget: PGtkWidget;
|
|
cell_area: PGdkRectangle; x_offset, y_offset, width, height: pgint); cdecl;
|
|
var
|
|
CellClass: PLCLIntfCellRendererClass;
|
|
AWinControl: TWinControl;
|
|
ItemIndex: Integer;
|
|
Msg: TLMMeasureItem;
|
|
MeasureItemStruct: TMeasureItemStruct;
|
|
begin
|
|
CellClass:=PLCLIntfCellRendererClass(gtk_object_get_class(cell));
|
|
CellClass^.DefaultGtkGetSize(cell, Widget, cell_area, x_offset, y_offset,
|
|
width, height);
|
|
// DebugLn(['LCLIntfCellRenderer_GetSize ',GetWidgetDebugReport(Widget)]);
|
|
AWinControl := GetControl(cell, widget);
|
|
if [csDestroying,csLoading]*AWinControl.ComponentState<>[] then exit;
|
|
|
|
if AWinControl is TCustomListbox then
|
|
if TCustomListbox(AWinControl).Style < lbOwnerDrawFixed then
|
|
exit;
|
|
if AWinControl is TCustomCombobox then
|
|
if not TCustomCombobox(AWinControl).Style.IsVariable then
|
|
exit;
|
|
|
|
ItemIndex := GetItemIndex(PLCLIntfCellRenderer(cell), Widget);
|
|
|
|
if ItemIndex < 0 then
|
|
ItemIndex := 0;
|
|
|
|
MeasureItemStruct.itemID := UINT(ItemIndex);
|
|
MeasureItemStruct.itemWidth := UINT(width^);
|
|
MeasureItemStruct.itemHeight := UINT(height^);
|
|
Msg.Msg := LM_MEASUREITEM;
|
|
Msg.MeasureItemStruct := @MeasureItemStruct;
|
|
DeliverMessage(AWinControl, Msg);
|
|
width^ := gint(MeasureItemStruct.itemWidth);
|
|
height^ := gint(MeasureItemStruct.itemHeight);
|
|
end;
|
|
|
|
function GtkCellRendererStateToListViewDrawState(CellState: TGtkCellRendererState): TCustomDrawState;
|
|
begin
|
|
Result := [];
|
|
if CellState and GTK_CELL_RENDERER_SELECTED > 0 then Result := Result + [cdsSelected];
|
|
if CellState and GTK_CELL_RENDERER_PRELIT > 0 then Result := Result + [cdsHot];
|
|
if CellState and GTK_CELL_RENDERER_INSENSITIVE > 0 then Result := Result + [cdsDisabled, cdsGrayed];
|
|
if CellState and GTK_CELL_RENDERER_FOCUSED > 0 then Result := Result + [cdsFocused];
|
|
end;
|
|
|
|
procedure LCLIntfCellRenderer_Render(cell: PGtkCellRenderer; Window: PGdkWindow;
|
|
Widget: PGtkWidget; background_area: PGdkRectangle; cell_area: PGdkRectangle;
|
|
expose_area: PGdkRectangle; flags: TGtkCellRendererState); cdecl;
|
|
var
|
|
CellClass: PLCLIntfCellRendererClass;
|
|
AWinControl: TWinControl;
|
|
ItemIndex: Integer;
|
|
ColumnIndex: Integer;
|
|
AreaRect: TRect;
|
|
State: TOwnerDrawState;
|
|
Msg: TLMDrawListItem;
|
|
DCWidget: PGtkWidget;
|
|
LVTarget: TCustomDrawTarget;
|
|
LVStage: TCustomDrawStage;
|
|
LVState: TCustomDrawState;
|
|
LVSubItem: Integer;
|
|
TmpDC1,
|
|
TmpDC2: HDC;
|
|
SkipDefaultPaint: Boolean;
|
|
OwnerDrawnListView: Boolean;
|
|
begin
|
|
{DebugLn(['LCLIntfCellRenderer_Render cell=',dbgs(cell),
|
|
' ',GetWidgetDebugReport(Widget),' ',
|
|
' background_area=',dbgGRect(background_area),
|
|
' cell_area=',dbgGRect(cell_area),
|
|
' expose_area=',dbgGRect(expose_area)]);}
|
|
|
|
ColumnIndex := PLCLIntfCellRenderer(cell)^.ColumnIndex;
|
|
|
|
AWinControl := GetControl(cell, widget);
|
|
if (ColumnIndex = -1) and (AWinControl <> nil) and
|
|
(AWinControl.FCompStyle = csListView) then
|
|
ColumnIndex := 0;
|
|
|
|
OwnerDrawnListView := False;
|
|
if ColumnIndex > -1 then // listview
|
|
begin
|
|
OwnerDrawnListView := TCustomListViewAccess(AWinControl).OwnerDraw and
|
|
(TCustomListViewAccess(AWinControl).ViewStyle = vsReport);
|
|
|
|
AreaRect := Bounds(background_area^.x, background_area^.y,
|
|
background_area^.Width, background_area^.Height);
|
|
|
|
|
|
ItemIndex := GetItemIndex(PLCLIntfCellRenderer(cell), Widget);
|
|
|
|
if ItemIndex < 0 then
|
|
ItemIndex := 0;
|
|
|
|
if ColumnIndex > 0 then
|
|
LVTarget := dtSubItem
|
|
else
|
|
LVTarget := dtItem;
|
|
if AWinControl.FCompStyle = csListView then
|
|
LVSubItem := ColumnIndex
|
|
else
|
|
LVSubItem := ColumnIndex - 1;
|
|
LVStage := cdPrePaint;
|
|
LVState := GtkCellRendererStateToListViewDrawState(flags);
|
|
DCWidget:=Widget;
|
|
TmpDC1:=GTK2WidgetSet.CreateDCForWidget(DCWidget,Window,false);
|
|
TmpDC2 := TCustomListViewAccess(AWinControl).Canvas.Handle;
|
|
TCustomListViewAccess(AWinControl).Canvas.Handle := TmpDC1;
|
|
// paint
|
|
SkipDefaultPaint := cdrSkipDefault in TCustomListViewAccess(AWinControl).IntfCustomDraw(LVTarget, LVStage, ItemIndex, LVSubItem, LVState, @AreaRect);
|
|
|
|
if SkipDefaultPaint then
|
|
begin
|
|
GTK2WidgetSet.ReleaseDC(HWnd({%H-}PtrUInt(Widget)),TmpDC1);
|
|
TCustomListViewAccess(AWinControl).Canvas.Handle := TmpDC2;
|
|
if not OwnerDrawnListView then
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
// draw default
|
|
CellClass := PLCLIntfCellRendererClass(gtk_object_get_class(cell));
|
|
|
|
// do not call DefaultGtkRender when we are custom drawn listbox.issue #23093
|
|
if ColumnIndex < 0 then
|
|
begin
|
|
if [csDestroying,csLoading,csDesigning]*AWinControl.ComponentState<>[] then
|
|
AWinControl := nil;
|
|
if AWinControl is TCustomListbox then
|
|
if TCustomListbox(AWinControl).Style = lbStandard then
|
|
AWinControl := nil;
|
|
if AWinControl is TCustomCombobox then
|
|
AWinControl := nil;
|
|
end;
|
|
// do default draw only if we are not customdrawn.
|
|
if (ColumnIndex > -1) or ((ColumnIndex < 0) and (AWinControl = nil)) then
|
|
begin
|
|
if not OwnerDrawnListView then
|
|
CellClass^.DefaultGtkRender(cell, Window, Widget, background_area, cell_area,
|
|
expose_area, flags);
|
|
end;
|
|
|
|
if ColumnIndex < 0 then // is a listbox or combobox
|
|
begin
|
|
// send LM_DrawListItem message
|
|
AWinControl := GetControl(cell, widget);
|
|
if [csDestroying,csLoading]*AWinControl.ComponentState<>[] then exit;
|
|
|
|
// check if the LCL object wants item paint messages
|
|
if AWinControl is TCustomListbox then
|
|
if TCustomListbox(AWinControl).Style = lbStandard then
|
|
exit;
|
|
if AWinControl is TCustomCombobox then
|
|
if not TCustomCombobox(AWinControl).Style.IsOwnerDrawn then
|
|
exit;
|
|
|
|
// get itemindex and area
|
|
|
|
AreaRect := Bounds(background_area^.x, background_area^.y,
|
|
background_area^.Width, background_area^.Height);
|
|
|
|
ItemIndex := GetItemIndex(PLCLIntfCellRenderer(cell), Widget);
|
|
|
|
if ItemIndex < 0 then
|
|
ItemIndex := 0;
|
|
|
|
// collect state flags
|
|
State:=[odBackgroundPainted];
|
|
if (flags and GTK_CELL_RENDERER_SELECTED)>0 then
|
|
Include(State, odSelected);
|
|
if not GTK_WIDGET_SENSITIVE(Widget) then
|
|
Include(State, odInactive);
|
|
if GTK_WIDGET_HAS_DEFAULT(Widget) then
|
|
Include(State, odDefault);
|
|
if (flags and GTK_CELL_RENDERER_FOCUSED) <> 0 then
|
|
Include(State, odFocused);
|
|
|
|
if AWinControl is TCustomCombobox then begin
|
|
if TCustomComboBox(AWinControl).DroppedDown
|
|
and ((flags and GTK_CELL_RENDERER_PRELIT)>0) then
|
|
Include(State,odSelected);
|
|
end;
|
|
end
|
|
else // is a listview
|
|
begin
|
|
LVStage := cdPostPaint;
|
|
// paint
|
|
TCustomListViewAccess(AWinControl).IntfCustomDraw(LVTarget, LVStage, ItemIndex, LVSubItem, LVState, @AreaRect);
|
|
|
|
TCustomListViewAccess(AWinControl).Canvas.Handle := TmpDC2;
|
|
GTK2WidgetSet.ReleaseDC(HWnd({%H-}PtrUInt(Widget)),TmpDC1);
|
|
if not OwnerDrawnListView then
|
|
Exit;
|
|
end;
|
|
|
|
// ListBox and ComboBox
|
|
// create message and deliverFillChar(Msg,SizeOf(Msg),0);
|
|
if OwnerDrawnListView then
|
|
begin
|
|
// we are TListView (GtkTreeView) with OwnerDraw + vsReport
|
|
Msg.Msg := CN_DRAWITEM;
|
|
|
|
// collect state flags
|
|
State := [];
|
|
if (flags and GTK_CELL_RENDERER_SELECTED)>0 then
|
|
Include(State, odSelected);
|
|
if not GTK_WIDGET_SENSITIVE(Widget) then
|
|
Include(State, odInactive);
|
|
if GTK_WIDGET_HAS_DEFAULT(Widget) then
|
|
Include(State, odDefault);
|
|
if (flags and GTK_CELL_RENDERER_FOCUSED) <> 0 then
|
|
Include(State, odFocused);
|
|
|
|
AreaRect := Bounds(expose_area^.x, expose_area^.y,
|
|
expose_area^.Width, expose_area^.Height);
|
|
if gtk_tree_view_get_headers_visible(PGtkTreeView(Widget)) then
|
|
begin
|
|
inc(AreaRect.Top, background_area^.height);
|
|
inc(AreaRect.Bottom, background_area^.height);
|
|
end;
|
|
end else
|
|
Msg.Msg:=LM_DrawListItem;
|
|
New(Msg.DrawListItemStruct);
|
|
try
|
|
FillChar(Msg.DrawListItemStruct^,SizeOf(TDrawListItemStruct),0);
|
|
with Msg.DrawListItemStruct^ do
|
|
begin
|
|
ItemID:=UINT(ItemIndex);
|
|
Area:=AreaRect;
|
|
// DebugLn(['LCLIntfCellRenderer_Render Widget=',GetWidgetDebugReport(Widget^.parent),' Area=',dbgs(Area)]);
|
|
DCWidget:=Widget;
|
|
if (DCWidget^.parent<>nil) and
|
|
(GtkWidgetIsA(DCWidget^.parent,gtk_menu_item_get_type)) then
|
|
begin
|
|
// the Widget is a sub widget of a menu item
|
|
// -> allow the LCL to paint over the whole menu item
|
|
DCWidget := DCWidget^.parent;
|
|
Area:=Rect(0,0,DCWidget^.allocation.width,DCWidget^.allocation.height);
|
|
end;
|
|
DC := GTK2WidgetSet.CreateDCForWidget(DCWidget,Window,false);
|
|
ItemState:=State;
|
|
end;
|
|
DeliverMessage(AWinControl, Msg);
|
|
GTK2WidgetSet.ReleaseDC(HWnd({%H-}PtrUInt(Widget)),Msg.DrawListItemStruct^.DC);
|
|
finally
|
|
Dispose(Msg.DrawListItemStruct);
|
|
end;
|
|
|
|
//DebugLn(['LCLIntfCellRenderer_Render END ',DbgSName(LCLObject)]);
|
|
end;
|
|
|
|
procedure LCLIntfCellRenderer_ClassInit(aClass: Pointer); cdecl;
|
|
//aClass: PLCLIntfCellRendererClass
|
|
var
|
|
LCLClass: PLCLIntfCellRendererClass;
|
|
RendererClass: PGtkCellRendererClass;
|
|
begin
|
|
//DebugLn(['LCLIntfCellRenderer_ClassInit ']);
|
|
LCLClass := PLCLIntfCellRendererClass(aClass);
|
|
RendererClass := GTK_CELL_RENDERER_CLASS(aClass);
|
|
LCLClass^.DefaultGtkGetSize := RendererClass^.get_size;
|
|
LCLClass^.DefaultGtkRender := RendererClass^.render;
|
|
RendererClass^.get_size := @LCLIntfCellRenderer_GetSize;
|
|
RendererClass^.render := @LCLIntfCellRenderer_Render;
|
|
end;
|
|
|
|
procedure LCLIntfCellRenderer_Init({%H-}Instance:PGTypeInstance;
|
|
{%H-}theClass: Pointer); cdecl;
|
|
// Instance: PLCLIntfCellRenderer;
|
|
// theClass: PLCLIntfCellRendererClass
|
|
begin
|
|
//DebugLn(['LCLIntfCellRenderer_Init ']);
|
|
end;
|
|
|
|
function LCLIntfCellRenderer_GetType: TGtkType;
|
|
const
|
|
CR_NAME = 'LCLIntfCellRenderer';
|
|
crType: TGtkType = 0;
|
|
crInfo: TGTKTypeInfo = (
|
|
type_name: CR_NAME;
|
|
object_size: SizeOf(TLCLIntfCellRenderer)+100; // a TLCLIntfCellRenderer
|
|
class_size: SizeOf(TLCLIntfCellRendererClass)+100;
|
|
class_init_func: @LCLIntfCellRenderer_ClassInit;
|
|
object_init_func : @LCLIntfCellRenderer_Init;
|
|
reserved_1: nil;
|
|
reserved_2: nil;
|
|
base_class_init_func: nil;
|
|
);
|
|
var
|
|
IID: TGUID;
|
|
S: AnsiString;
|
|
begin
|
|
if (crType = 0)
|
|
then begin
|
|
// patch by tk: in case of shared library we must create unique name
|
|
if CreateGUID(IID) = 0 then
|
|
S := Format('LCL%d%d%d', [Word(IID.time_low), Word(IID.time_low shr 16), IID.time_mid]);
|
|
crType := gtk_type_from_name(PAnsiChar(S));
|
|
if crType = 0 then begin
|
|
crInfo.type_name:=PAnsiChar(S);
|
|
crType := gtk_type_unique(gtk_cell_renderer_text_get_type, @crInfo);
|
|
end;
|
|
end;
|
|
Result := crType;
|
|
end;
|
|
|
|
function LCLIntfCellRenderer_New: PGtkCellRenderer;
|
|
begin
|
|
Result := g_object_new(LCLIntfCellRenderer_GetType, nil,[]);
|
|
end;
|
|
|
|
procedure LCLIntfCellRenderer_CellDataFunc(cell_layout:PGtkCellLayout;
|
|
cell: PGtkCellRenderer; tree_model: PGtkTreeModel; iter: PGtkTreeIter;
|
|
data: gpointer); cdecl;
|
|
var
|
|
LCLCellRenderer: PLCLIntfCellRenderer absolute cell;
|
|
WidgetInfo: PWidgetInfo;
|
|
APath: PGtkTreePath;
|
|
Str: String;
|
|
ListColumn: TListColumn;
|
|
ListItem: TListItem;
|
|
Value: TGValue;
|
|
begin
|
|
if G_IS_OBJECT(cell) = false then
|
|
exit;
|
|
|
|
FillByte(Value{%H-},SizeOf(Value),0);
|
|
APath := gtk_tree_model_get_path(tree_model,iter);
|
|
LCLCellRenderer^.Index := gtk_tree_path_get_indices(APath)^;
|
|
LCLCellRenderer^.ColumnIndex := -1;
|
|
gtk_tree_path_free(APath);
|
|
|
|
WidgetInfo := PWidgetInfo(data);
|
|
// DebugLn(['LCLIntfCellRenderer_CellDataFunc stamp=',iter^.stamp,' tree_model=',dbgs(tree_model),' cell=',dbgs(cell),' WidgetInfo=',WidgetInfo <> nil,' Time=',TimeToStr(Now)]);
|
|
|
|
if (WidgetInfo <> nil) and
|
|
(WidgetInfo^.LCLObject is TCustomComboBox) and
|
|
not (TCustomComboBox(WidgetInfo^.LCLObject).Style.HasEditBox) and
|
|
not (TCustomComboBox(WidgetInfo^.LCLObject).DroppedDown) then
|
|
begin
|
|
g_value_init(@value, G_TYPE_UINT);
|
|
g_value_set_uint(@value, 0);
|
|
g_object_get_property(PgObject(cell), 'ypad', @Value);
|
|
g_value_set_uint(@value, 0);
|
|
g_object_set_property(PGObject(cell), 'ypad', @Value);
|
|
g_value_unset(@value);
|
|
end else
|
|
if (WidgetInfo <> nil) and (WidgetInfo^.LCLObject.InheritsFrom(TCustomListView)) then
|
|
begin
|
|
// DebugLn(['LCLIntfCellRenderer_CellDataFunc stamp=',iter^.stamp,' tree_model=',dbgs(tree_model),' cell=',dbgs(cell),' WidgetInfo=',WidgetInfo <> nil,' Time=',TimeToStr(Now)]);
|
|
gtk_tree_model_get(tree_model, iter, [0, @ListItem, -1]);
|
|
if (ListItem = nil) and TCustomListView(WidgetInfo^.LCLObject).OwnerData then
|
|
ListItem := TCustomListView(WidgetInfo^.LCLObject).Items[LCLCellRenderer^.Index];
|
|
if ListItem = nil then
|
|
Exit;
|
|
ListColumn := TListColumn(g_object_get_data(G_OBJECT(cell_layout), 'TListColumn'));
|
|
if ListColumn = nil then
|
|
LCLCellRenderer^.ColumnIndex := -1
|
|
else
|
|
LCLCellRenderer^.ColumnIndex := ListColumn.Index;
|
|
|
|
if LCLCellRenderer^.ColumnIndex <= 0 then
|
|
Str := ListItem.Caption
|
|
else
|
|
if ListColumn.Index-1 <= ListItem.SubItems.Count-1 then
|
|
Str := ListItem.SubItems.Strings[LCLCellRenderer^.ColumnIndex-1];
|
|
|
|
g_value_init(@value, G_TYPE_STRING);
|
|
g_value_set_string(@value, PChar(Str));
|
|
g_object_set_property(PGObject(cell), 'text', @Value);
|
|
g_value_unset(@value);
|
|
end;
|
|
|
|
// DebugLn(['LCLIntfCellRenderer_CellDataFunc ItemIndex=',LCLCellRenderer^.Index]);
|
|
end;
|
|
|
|
end.
|