lazarus/lcl/interfaces/gtk/gtkwinapi.inc

10266 lines
318 KiB
PHP

{%MainUnit gtkint.pp}
{ $Id$ }
{******************************************************************************
All GTK Winapi implementations.
Initial Revision : Sat Nov 13 12:53:53 1999
!! Keep alphabetical !!
Support routines go to gtkproc.pp
******************************************************************************
Implementation
******************************************************************************
*****************************************************************************
* *
* This file is part of the Lazarus Component Library (LCL) *
* *
* See the file COPYING.modifiedLGPL.txt, 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. *
* *
*****************************************************************************
}
{$IFOPT C-}
// Uncomment for local trace
// {$C+}
// {$DEFINE ASSERT_IS_ON}
{$EndIf}
const
BOOL_TEXT: array[Boolean] of string = ('False', 'True');
//##apiwiz##sps## // Do not remove
{------------------------------------------------------------------------------
Method: Arc
Params: left, top, right, bottom, angle1, angle2
Returns: Nothing
Use Arc to draw an elliptically curved line with the current Pen.
The angles angle1 and angle2 are 1/16th of a degree. For example, a full
circle equals 5760 (16*360). Positive values of Angle and AngleLength mean
counter-clockwise while negative values mean clockwise direction.
Zero degrees is at the 3'o clock position.
Angle1 is the starting angle. Angle2 is relative to Angle1 (added).
Example:
Angle1 = 10*16, Angle2 = 30*16 will draw an arc from 10 to 40 degree.
------------------------------------------------------------------------------}
function TGtkWidgetSet.Arc(DC: HDC;
left, top, right, bottom, angle1, angle2: Integer): Boolean;
var
DevCtx: TGtkDeviceContext absolute DC;
DCOrigin: TPoint;
Angle: Integer;
begin
Result := IsValidDC(DC);
if not Result then Exit;
// Draw outline
DevCtx.SelectPenProps;
if not (dcfPenSelected in DevCtx.Flags)
then begin
Result := False;
Exit;
end;
if DevCtx.IsNullPen then Exit;
if DevCtx.HasTransf then
begin
DevCtx.TransfRect(Left, Top, Right, Bottom);
DevCtx.TransfNormalize(Left, Right);
DevCtx.TransfNormalize(Top, Bottom);
// we must convert angles too because of possible negative axis orientations
Angle := Angle1 + Angle2;
DevCtx.TransfAngles(Angle1, Angle);
Angle2 := Angle - Angle1;
end;
DCOrigin := DevCtx.Offset;
inc(Left, DCOrigin.X);
inc(Top, DCOrigin.Y);
inc(Right, DCOrigin.X);
inc(Bottom, DCOrigin.Y);
{$IFDEF DebugGDKTraps}BeginGDKErrorTrap;{$ENDIF}
gdk_draw_arc(DevCtx.Drawable, DevCtx.GC, 0, left, top, right - left, bottom - top,
Angle1*4, Angle2*4);
{$IFDEF DebugGDKTraps}EndGDKErrorTrap;{$ENDIF}
end;
{------------------------------------------------------------------------------
Method: AngleChord
Params: DC, x1, y1, x2, y2, angle1, angle2
Returns: Nothing
Use AngleChord to draw a filled Chord-shape on the canvas. The angles angle1
and angle2 are 1/16th of a degree. For example, a full circle equals 5760
16*360). Positive values of Angle and AngleLength mean counter-clockwise while
negative values mean clockwise direction. Zero degrees is at the 3'o clock
position.
------------------------------------------------------------------------------}
function TGtkWidgetSet.AngleChord(DC: HDC;
x1, y1, x2, y2, angle1, angle2: Integer): Boolean;
begin
Result := inherited AngleChord(DC, x1, y1, x2, y2, angle1, angle2);
end;
{------------------------------------------------------------------------------
Function: BeginPaint
Params:
Returns:
------------------------------------------------------------------------------}
function TGtkWidgetSet.BeginPaint(Handle: hWnd; var PS : TPaintStruct) : hdc;
var
Widget: PGtkWidget;
Info: PWidgetInfo;
{$IFDEF Gtk1}
IsDoubleBuffered: Boolean;
TargetObject: TObject;
PaintWidget: Pointer;
{$ELSE}
DC: TGtkDeviceContext;
{$ENDIF}
begin
Widget:=PGtkWidget(Handle);
Info:=GetWidgetInfo(Widget,false);
if Info<>nil then
Inc(Info^.PaintDepth);
{$IFDEF Gtk1}
TargetObject:=GetNearestLCLObject(Widget);
IsDoubleBuffered:=(TargetObject is TWinControl)
and TWinControl(TargetObject).DoubleBuffered;
// check if Handle is the paint widget of the LCL component
if IsDoubleBuffered then begin
PaintWidget:=GetFixedWidget(PGtkWidget(TWinControl(TargetObject).Handle));
IsDoubleBuffered:=(PaintWidget=Widget);
//if not IsDoubleBuffered then begin
// DebugLn('TGtkWidgetSet.BeginPaint Not the paint widget: ',
// TWinControl(TargetObject).Name,':',TWinControl(TargetObject).ClassName,
// ' PaintWidget=',GetWidgetClassName(PaintWidget),
// ' Widget=',GetWidgetClassName(Widget));
//end;
end;
{$IFNDEF UseGTKDoubleBuf}
IsDoubleBuffered:=false;
{$ENDIF}
if IsDoubleBuffered then
PS.hDC:=GetDoubleBufferedDC(Handle)
else
PS.hDC:=GetDC(Handle);
{$ELSE below: not GTK1}
PS.hDC:=GetDC(Handle);
DC:=TGtkDeviceContext(PS.hDC);
DC.PaintRectangle:=PS.rcPaint;
{$ENDIF}
Result := PS.hDC;
end;
{------------------------------------------------------------------------------
Function: BitBlt
Params: DestDC: The destination devicecontext
X, Y: The left/top corner of the destination rectangle
Width, Height: The size of the destination rectangle
SrcDC: The source devicecontext
XSrc, YSrc: The left/top corner of the source rectangle
Rop: The raster operation to be performed
Returns: True if succesful
The BitBlt function copies a bitmap from a source context into a destination
context using the specified raster operation.
------------------------------------------------------------------------------}
function TGtkWidgetSet.BitBlt(DestDC: HDC; X, Y, Width, Height: Integer;
SrcDC: HDC; XSrc, YSrc: Integer; Rop: DWORD): Boolean;
begin
Result := StretchBlt(DestDC, X, Y, Width, Height, SrcDC, XSrc, YSrc, Width,
Height, ROP);
end;
{------------------------------------------------------------------------------
Function: CallNextHookEx
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.CallNextHookEx(hhk : HHOOK; ncode : Integer; wParam: WParam; lParam : LParam) : Integer;
begin
Result := 0;
//TODO: Does anything need to be done here?
//DebugLn('Trace:!!!!!!!!!!!!!!!!!!');
//DebugLn('Trace:!!!!!!!!!!!!!!!!!!');
//DebugLn('Trace:TODO: CALLNEXTHOOKEX in gtkwinapi.inc');
//DebugLn('Trace:!!!!!!!!!!!!!!!!!!');
//DebugLn('Trace:!!!!!!!!!!!!!!!!!!');
end;
{------------------------------------------------------------------------------
Function: CallWindowProc
Params: lpPrevWndFunc:
Handle:
Msg:
wParam:
lParam:
Returns:
------------------------------------------------------------------------------}
function TGtkWidgetSet.CallWindowProc(lpPrevWndFunc : TFarProc; Handle : HWND;
Msg : UINT; wParam: WParam; lParam : LParam) : Integer;
var
Proc : TWndMethod;
Mess : TLMessage;
P : Pointer;
begin
Result := -1;
if Handle = 0 then Exit;
Result := -1;
P := nil;
P := gtk_object_get_data(pgtkobject(Handle),'WNDPROC');
if P <> nil then
Proc := TWndMethod(P^)
else
Exit;
Mess.msg := msg;
Mess.LParam := LParam;
Mess.WParam := WParam;
Proc(Mess);
Result := Mess.Result;
end;
{------------------------------------------------------------------------------
Function: ClientToScreen
Params: Handle : HWND; var P : TPoint
Returns: true on success
Converts the client-area coordinates of P to screen coordinates.
------------------------------------------------------------------------------}
function TGtkWidgetSet.ClientToScreen(Handle : HWND; var P : TPoint) : Boolean;
var
Position: TPoint;
LCLObject: TObject;
List: PGList;
i: Integer;
Pt: TPoint;
Adjustment: PGtkAdjustment;
Scrolled: PGtkScrolledWindow;
begin
if Handle = 0 then
begin
Position.X := 0;
Position.Y := 0;
end else
begin
Position := GetWidgetClientOrigin(PGtkWidget(Handle));
LCLObject:=GetLCLObject(PGtkWidget(Handle));
if (LCLObject <> nil) and (LCLObject is TScrollingWinControl) then
begin
List := gtk_container_children(PGtkContainer(PGtkWidget(Handle)));
if (g_list_length(List) > 0) and
GTK_IS_SCROLLED_WINDOW(g_list_nth_data(List, 0)) then
begin
Scrolled := PGtkScrolledWindow(g_list_nth_data(List, 0));
Pt := Point(0, 0);
Adjustment := gtk_scrolled_window_get_vadjustment(Scrolled);
if Adjustment <> nil then
Pt.Y := Round(Adjustment^.value);
Adjustment := gtk_scrolled_window_get_hadjustment(Scrolled);
if Adjustment <> nil then
Pt.X := Round(Adjustment^.value);
dec(Position.X, Pt.X);
dec(Position.Y, Pt.Y);
end;
glib.g_list_free(List);
end;
end;
Inc(P.X, Position.X);
Inc(P.Y, Position.Y);
//DebugLn(Format('Trace: [GTKObject.ClientToScreen] Handle: 0x%x --> (%d, %d)', [Integer(Handle), P.X, P.y]));
Result := True;
end;
{------------------------------------------------------------------------------
Function: ClipboardFormatToMimeType
Params: FormatID - a registered format identifier (0 is invalid)
Returns: the corresponding mime type as string
------------------------------------------------------------------------------}
function TGtkWidgetSet.ClipboardFormatToMimeType(
FormatID: TClipboardFormat): string;
var p: PChar;
begin
if FormatID<>0 then begin
p:=gdk_atom_name(FormatID);
Result:=StrPas(p);
g_free(p);
end else
Result:='';
end;
{------------------------------------------------------------------------------
Function: ClipboardGetData
Params: ClipboardType
FormatID - a registered format identifier (0 is invalid)
Stream - If format is available, it will be appended to this stream
Returns: true on success
------------------------------------------------------------------------------}
function TGtkWidgetSet.ClipboardGetData(ClipboardType: TClipboardType;
FormatID: TClipboardFormat; Stream: TStream): boolean;
type
PGdkAtom = ^TGdkAtom;
var
FormatAtom, FormatTry: TGdkAtom;
SupportedCnt, i: integer;
SupportedFormats: PGdkAtom;
SelData: TGtkSelectionData;
CompoundTextList: PPGChar;
CompoundTextCount: integer;
function IsFormatSupported(CurFormat: TGdkAtom): boolean;
var a: integer;
AllID: TGdkAtom;
begin
//DebugLn('IsFormatSupported CurFormat=',dbgs(CurFormat),' SupportedCnt=',dbgs(SupportedCnt));
if CurFormat=0 then begin
Result:=false;
exit;
end;
if SupportedCnt<0 then begin
Result:=false;
AllID:=gdk_atom_intern('TARGETS',GdkFalse);
SelData:=RequestSelectionData(ClipboardWidget,ClipboardType,AllID);
{DebugLn('IsFormatSupported A ',Dbgs(SelData.Selection),
' ',HexStr(Cardinal(ClipboardTypeAtoms[ClipboardType]),8),
' SelData.Target='+dbgs(SelData.Target),' AllID='+dbgs(AllID),
' SelData.TheType='+dbgs(SelData.TheType)+' ATOM='+dbgs(gdk_atom_intern('ATOM',0))+' Name="'+GdkAtomToStr(SelData.TheType)+'"',
' SelData.Length='+dbgs(SelData.Length),
' SelData.Format='+dbgs(SelData.Format)
);}
if (SelData.Selection<>ClipboardTypeAtoms[ClipboardType])
or (SelData.Target<>AllID)
or (SelData.{$IfDef GTK2}_Type{$Else}theType{$EndIf}<>gdk_atom_intern('ATOM',GdkFalse)) then begin
SupportedCnt:=0;
exit;
end;
SupportedCnt:=SelData.Length div (SelData.Format shr 3);
SupportedFormats:=PGdkAtom(SelData.Data);
//DebugLn('IsFormatSupported SupportedCnt=',dbgs(SupportedCnt));
{a:=SupportedCnt-1;
while (a>=0) do begin
debugln(' ',dbgs(a),' ',GdkAtomToStr(SupportedFormats[a]),' "',p,'"');
dec(a);
end;}
end;
a:=SupportedCnt-1;
while (a>=0) and (SupportedFormats[a]<>CurFormat) do dec(a);
Result:=(a>=0);
end;
begin
{$IfDef DEBUG_CLIPBOARD}
DebugLn('[TGtkWidgetSet.ClipboardGetData] A ClipboardWidget=',Dbgs(ClipboardWidget),' FormatID=',ClipboardFormatToMimeType(FormatID),' Now=',dbgs(Now));
{$EndIf}
Result:=false;
if (FormatID=0) or (Stream=nil) then exit;
if not (ClipboardType in [ctPrimarySelection,ctSecondarySelection,ctClipboard])
then exit;
// request the data from the selection owner
SupportedCnt:=-1;
SupportedFormats:=nil;
FillChar(SelData,SizeOf(TGtkSelectionData),0);
try
FormatAtom:=FormatID;
if (FormatAtom=gdk_atom_intern('text/plain',GdkTrue)) then begin
FormatAtom:=0;
// text/plain is supported in various formats in gtk
FormatTry:=gdk_atom_intern('COMPOUND_TEXT',GdkFalse);
if IsFormatSupported(FormatTry) then
FormatAtom:=FormatTry;
// The COMPOUND_TEXT format can be converted and is therefore
// used as default for 'text/plain'
if (SupportedCnt=0) then
FormatAtom:=gdk_atom_intern('COMPOUND_TEXT',GdkFalse);
// then check for UTF8 text format 'UTF8_STRING'
FormatTry:=gdk_atom_intern('UTF8_STRING',GdkFalse);
if IsFormatSupported(FormatTry) then
FormatAtom:=FormatTry;
// then check for simple text format 'text/plain'
FormatTry:=gdk_atom_intern('text/plain',GdkFalse);
if (FormatAtom=0) and IsFormatSupported(FormatTry) then
FormatAtom:=FormatTry;
// then check for simple text format STRING
FormatTry:=gdk_atom_intern('STRING',GdkFalse);
if (FormatAtom=0) and IsFormatSupported(FormatTry) then
FormatAtom:=FormatTry;
// check for some other formats that can be interpreted as text
FormatTry:=gdk_atom_intern('FILE_NAME',GdkTrue);
if (FormatAtom=0) and IsFormatSupported(FormatTry) then
FormatAtom:=FormatTry;
FormatTry:=gdk_atom_intern('HOST_NAME',GdkTrue);
if (FormatAtom=0) and IsFormatSupported(FormatTry) then
FormatAtom:=FormatTry;
FormatTry:=gdk_atom_intern('USER',GdkTrue);
if (FormatAtom=0) and IsFormatSupported(FormatTry) then
FormatAtom:=FormatTry;
// the TEXT format is not reliable, but it should be supported
FormatTry:=gdk_atom_intern('TEXT',GdkFalse);
if (FormatAtom=0) and IsFormatSupported(FormatTry) then
FormatAtom:=FormatTry;
end;
{$IfDef DEBUG_CLIPBOARD}
DebugLn('[TGtkWidgetSet.ClipboardGetData] B Format=',ClipboardFormatToMimeType(FormatAtom),' FormatAtom=',dbgs(FormatAtom),' Now=',dbgs(Now));
{$EndIf}
if FormatAtom=0 then exit;
// request data from owner
SelData:=RequestSelectionData(ClipboardWidget,ClipboardType,FormatAtom);
{$IfDef DEBUG_CLIPBOARD}
DebugLn('[TGtkWidgetSet.ClipboardGetData] C Length=',dbgs(SelData.Length),' Now=',dbgs(Now),' ',
' SelData.Selection=',dbgs(SelData.Selection),' SelData.Length=',dbgs(SelData.Length));
{$EndIf}
if (SelData.Selection<>ClipboardTypeAtoms[ClipboardType])
or (SelData.Target<>FormatAtom) then begin
{$IfDef DEBUG_CLIPBOARD}
DebugLn('[TGtkWidgetSet.ClipboardGetData] REQUESTED FORMAT NOT SUPPORTED Length=',dbgs(SelData.Length));
{$ENDIF}
exit;
end;
// write data to stream
if (SelData.Data<>nil) and (SelData.Length>0) then begin
if (FormatID=gdk_atom_intern('text/plain',GdkTrue)) then begin
// the lcl expects the return format as simple text
// transform if necessary
if FormatAtom=gdk_atom_intern('COMPOUND_TEXT',GdkTrue) then begin
CompoundTextCount:=gdk_text_property_to_text_list(SelData.{$IfDef GTK2}_Type{$Else}theType{$EndIf},
SelData.Format,SelData.Data,SelData.Length,{$IfDef GTK1}@{$EndIf}CompoundTextList);
{$IfDef DEBUG_CLIPBOARD}
DebugLn('[TGtkWidgetSet.ClipboardGetData] D CompoundTextCount=',dbgs(CompoundTextCount),' Now=',dbgs(Now));
{$EndIf}
for i:=0 to CompoundTextCount-1 do
if (CompoundTextList[i]<>nil) then
Stream.Write(CompoundTextList[i]^,StrLen(CompoundTextList[i]));
gdk_free_text_list(CompoundTextList);
end else
Stream.Write(SelData.Data^,SelData.Length);
end else begin
Stream.Write(SelData.Data^,SelData.Length);
end;
end;
{$IfDef DEBUG_CLIPBOARD}
DebugLn('[TGtkWidgetSet.ClipboardGetData] END ',' Now=',dbgs(Now));
{$EndIf}
Result:=true;
finally
if SupportedFormats<>nil then FreeMem(SupportedFormats);
if SelData.Data<>nil then FreeMem(SelData.Data);
end;
end;
{------------------------------------------------------------------------------
Function: ClipboardGetFormats
Params: ClipboardType
Returns: true on success
Count contains the number of supported formats
List is an array of TClipboardType
! List will be created. You must free it yourself with FreeMem(List) !
------------------------------------------------------------------------------}
function TGtkWidgetSet.ClipboardGetFormats(ClipboardType: TClipboardType;
var Count: integer; var List: PClipboardFormat): boolean;
type
PGdkAtom = ^TGdkAtom;
var
AllID: TGdkAtom;
FormatAtoms: PGdkAtom;
Cnt, i: integer;
AddTextPlain: boolean;
SelData: TGtkSelectionData;
function IsFormatSupported(CurFormat: TGdkAtom): boolean;
var a: integer;
begin
if CurFormat<>0 then begin
for a:=0 to Cnt-1 do begin
{$IfDef DEBUG_CLIPBOARD}
DebugLn(' IsFormatSupported ',dbgs(CurFormat),' ',dbgs(FormatAtoms[a]));
{$EndIf}
if FormatAtoms[a]=CurFormat then begin
Result:=true;
exit;
end;
end;
end;
Result:=false;
end;
function IsFormatSupported(Formats: TGtkClipboardFormats): boolean;
var Format: TGtkClipboardFormat;
begin
for Format:=Low(TGtkClipboardFormat) to High(TGtkClipboardFormat) do
if (Format in Formats)
and (IsFormatSupported(
gdk_atom_intern(PGChar(GtkClipboardFormatName[Format]),GdkTrue)))
then begin
Result:=true;
exit;
end;
Result:=false;
end;
begin
{$IfDef DEBUG_CLIPBOARD}
DebugLn('[TGtkWidgetSet.ClipboardGetFormats] A ClipboardWidget=',Dbgs(ClipboardWidget),' Now=',dbgs(Now));
{$EndIf}
Result:=false;
Count:=0;
List:=nil;
if not (ClipboardType in [ctPrimarySelection,ctSecondarySelection,ctClipboard])
then exit;
// request the list of supported formats from the selection owner
AllID:=gdk_atom_intern('TARGETS',GdkFalse);
SelData:=RequestSelectionData(ClipboardWidget,ClipboardType,AllID);
try
{$IfDef DEBUG_CLIPBOARD}
DebugLn('[TGtkWidgetSet.ClipboardGetFormats] Checking TARGETS answer ',
' selection: '+dbgs(SelData.Selection)+'='+dbgs(ClipboardTypeAtoms[ClipboardType])+
' "'+GdkAtomToStr(SelData.Selection)+'"',
' target: '+dbgs(SelData.Target),'=',dbgs(AllID),
' "'+GdkAtomToStr(SelData.Target),'"',
' theType: '+dbgs(SelData.{$IFDEF Gtk1}theType{$ELSE}_type{$ENDIF})+'='+dbgs(gdk_atom_intern('ATOM',GdkFalse))+
' "'+GdkAtomToStr(SelData.{$IFDEF Gtk1}theType{$ELSE}_type{$ENDIF})+'"',
' Length='+dbgs(SelData.Length),
' Format='+dbgs(SelData.Format),
' Data='+Dbgs(SelData.Data),
' Now='+dbgs(Now)
);
{$EndIf}
if (SelData.Selection<>ClipboardTypeAtoms[ClipboardType])
or (SelData.Target<>AllID)
or (SelData.Format<=0)
or ((SelData.{$IfDef GTK2}_Type{$Else}TheType{$EndIf}<>gdk_atom_intern('ATOM',GdkFalse))
and (SelData.{$IfDef GTK2}_Type{$Else}TheType{$EndIf}<>AllID))
then
exit;
Cnt:=SelData.Length div (SelData.Format shr 3);
if (SelData.Data<>nil) and (Cnt>0) then begin
Count:=Cnt;
FormatAtoms:=PGdkAtom(SelData.Data);
// add transformable lcl formats
// for example: the lcl expects text as 'text/plain', but gtk applications
// also know 'TEXT' and 'STRING'. These formats can automagically
// transformed into the lcl format, so the lcl format is also supported
// and will be added to the list
AddTextPlain:=false;
if (not IsFormatSupported(gdk_atom_intern('text/plain',GdkTrue)))
and (IsFormatSupported([gfCOMPOUND_TEXT,gfTEXT,gfSTRING,gfFILE_NAME,
gfHOST_NAME,gfUSER]))
then begin
AddTextPlain:=true;
inc(Count);
end;
// copy normal supported formats
GetMem(List,SizeOf(TClipboardFormat)*Count);
i:=0;
while (i<Cnt) do begin
{$IfDef DEBUG_CLIPBOARD}
DebugLn('[TGtkWidgetSet.ClipboardGetFormats] Supported formats: ',
dbgs(i)+'/'+dbgs(Cnt),': ',dbgs(FormatAtoms[i]));
DebugLn(' MimeType="',ClipboardFormatToMimeType(FormatAtoms[i]),'"');
{$EndIf}
List[i]:=FormatAtoms[i];
inc(i);
end;
// add all lcl formats that the gtk-interface can transform from the
// supported formats
if AddTextPlain then begin
List[i]:=gdk_atom_intern('text/plain',GdkFalse);
inc(i);
end;
end;
finally
if SelData.Data<>nil then FreeMem(SelData.Data);
end;
Result:=true;
end;
{------------------------------------------------------------------------------
Function: ClipboardGetOwnerShip
Params: ClipboardType
OnRequestProc - TClipboardRequestEvent is defined in LCLIntf.pp
If OnRequestProc is nil the onwership will end.
FormatCount - number of formats
Formats - array of TClipboardFormat. The supported formats the owner
provides.
Returns: true on success
Sets the supported formats and requests ownership for the clipboard.
Each time the clipboard is read the OnRequestProc will be executed.
If someone else requests the ownership, the OnRequestProc will be executed
with the invalid FormatID 0 to notify the old owner of the lost of ownership.
------------------------------------------------------------------------------}
function TGtkWidgetSet.ClipboardGetOwnerShip(ClipboardType: TClipboardType;
OnRequestProc: TClipboardRequestEvent; FormatCount: integer;
Formats: PClipboardFormat): boolean;
var TargetEntries: PGtkTargetEntry;
function IsFormatSupported(FormatID: TGdkAtom): boolean;
var i: integer;
begin
if FormatID=0 then begin
Result:=false;
exit;
end;
i:=FormatCount-1;
while (i>=0) and (Formats[i]<>FormatID) do dec(i);
Result:=(i>=0);
end;
procedure AddTargetEntry(var Index: integer; const FormatName: string);
begin
{$IfDef DEBUG_CLIPBOARD}
DebugLn(' AddTargetEntry ',FormatName);
{$EndIf}
TargetEntries[Index].Target := StrAlloc(Length(FormatName) + 1);
StrPCopy(TargetEntries[Index].Target, FormatName);
TargetEntries[Index].flags:=0;
TargetEntries[Index].Info:=Index;
inc(Index);
end;
{function TGtkWidgetSet.ClipboardGetOwnerShip(ClipboardType: TClipboardType;
OnRequestProc: TClipboardRequestEvent; FormatCount: integer;
Formats: PClipboardFormat): boolean;}
var
TargetEntriesSize, i: integer;
gtkFormat: TGtkClipboardFormat;
ExpFormatCnt: integer;
OldClipboardWidget: PGtkWidget;
begin
if ClipboardType in [ctPrimarySelection, ctSecondarySelection, ctClipboard] then
begin
{$IfDef DEBUG_CLIPBOARD}
DebugLn('[TGtkWidgetSet.ClipboardGetOwnerShip] A');
{$EndIf}
ClipboardHandler[ClipboardType]:=nil;
Result:=false;
if (ClipboardWidget=nil) or (FormatCount=0) or (Formats=nil) then
begin
// end ownership
if (ClipBoardWidget <> nil)
and (GetControlWindow(ClipboardWidget)<>nil)
and (gdk_selection_owner_get(ClipboardTypeAtoms[ClipboardType]) =
GetControlWindow(ClipboardWidget))
then begin
gtk_selection_owner_set(nil,ClipboardTypeAtoms[ClipboardType],0);
end;
Result:=true;
exit;
end;
// registering targets
FreeClipboardTargetEntries(ClipboardType);
// the gtk-interface adds automatically some gtk formats unknown to the lcl
ExpFormatCnt:=FormatCount;
for gtkFormat:=Low(TGtkClipboardFormat) to High(TGtkClipboardFormat) do
ClipboardExtraGtkFormats[ClipboardType][gtkFormat]:=false;
{$IfDef DEBUG_CLIPBOARD}
DebugLn('[TGtkWidgetSet.ClipboardGetOwnerShip] B');
{$EndIf}
if IsFormatSupported(gdk_atom_intern('text/plain',GdkTrue)) then
begin
// lcl provides 'text/plain' and the gtk-interface will automatically
// provide some more text formats
ClipboardExtraGtkFormats[ClipboardType][gfCOMPOUND_TEXT]:=
not IsFormatSupported(
gdk_atom_intern(PGChar(GtkClipboardFormatName[gfCOMPOUND_TEXT]),GdkFalse));
ClipboardExtraGtkFormats[ClipboardType][gfSTRING]:=not IsFormatSupported(
gdk_atom_intern(PGChar(GtkClipboardFormatName[gfSTRING]),GdkFalse));
ClipboardExtraGtkFormats[ClipboardType][gfTEXT]:=not IsFormatSupported(
gdk_atom_intern(PGChar(GtkClipboardFormatName[gfTEXT]),GdkFalse));
end;
for gtkFormat:=Low(TGtkClipboardFormat) to High(TGtkClipboardFormat) do
if ClipboardExtraGtkFormats[ClipboardType][gtkFormat] then
inc(ExpFormatCnt);
// build TargetEntries
TargetEntriesSize:=SizeOf(TGtkTargetEntry) * ExpFormatCnt;
GetMem(TargetEntries,TargetEntriesSize);
FillChar(TargetEntries^,TargetEntriesSize,0);
i:=0;
while i<FormatCount do
AddTargetEntry(i,ClipboardFormatToMimeType(Formats[i]));
for gtkFormat:=Low(TGtkClipboardFormat) to High(TGtkClipboardFormat) do
if ClipboardExtraGtkFormats[ClipboardType][gtkFormat] then
AddTargetEntry(i,GtkClipboardFormatName[gtkFormat]);
// set the supported formats
ClipboardTargetEntries[ClipboardType]:=TargetEntries;
ClipboardTargetEntryCnt[ClipboardType]:=ExpFormatCnt;
// reset the clipboard widget (this will set the new target list)
OldClipboardWidget:=ClipboardWidget;
SetClipboardWidget(nil);
SetClipboardWidget(OldClipboardWidget);
// taking the ownership
{$IfDef DEBUG_CLIPBOARD}
DebugLn('[TGtkWidgetSet.ClipboardGetOwnerShip] C');
{$EndIf}
if gtk_selection_owner_set(ClipboardWidget,
ClipboardTypeAtoms[ClipboardType],0)=GdkFalse
then begin
{$IfDef DEBUG_CLIPBOARD}
DebugLn('[TGtkWidgetSet.ClipboardGetOwnerShip] D FAILED');
{$EndIf}
exit;
end;
{$IfDef DEBUG_CLIPBOARD}
DebugLn('[TGtkWidgetSet.ClipboardGetOwnerShip] YEAH, got it!');
{$EndIf}
ClipboardHandler[ClipboardType]:=OnRequestProc;
Result:=true;
end else
{ the gtk does not support this kind of clipboard, so the application can
have the ownership at any time. The TClipboard in clipbrd.pp has an
internal cache system, so that an application can use all types of
clipboards even if the underlying platform does not support it.
Of course this will only be a local clipboard, invisible to other
applications. }
Result:=true;
end;
{------------------------------------------------------------------------------
Function: ClipboardRegisterFormat
Params: AMimeType
Returns: the registered Format identifier (TClipboardFormat)
------------------------------------------------------------------------------}
function TGtkWidgetSet.ClipboardRegisterFormat(
const AMimeType:String): TClipboardFormat;
var AtomName: PChar;
begin
if Assigned(Application) then begin
AtomName:=PChar(AMimeType);
Result:=gdk_atom_intern(AtomName,GdkFalse);
end else
RaiseGDBException(
'ERROR: TGtkWidgetSet.ClipboardRegisterFormat gdk not initialized');
end;
{------------------------------------------------------------------------------
Function: CreateBitmap
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.CreateBitmap(Width, Height: Integer; Planes, BitCount: Longint; BitmapBits: Pointer): HBITMAP;
const
MIN_LOADER_HEADER_SIZE = 128;
type
// the loader internally used starts decoding the header after 128 bytes.
// by adding dummy bytes and adjusting the data offset, we make sure that we
// we write atleast 128 bytes
TBitmapHeader = packed record
FileHeader: tagBitmapFileHeader;
InfoHeader: tagBitmapInfoHeader;
Dummy: array[1..MIN_LOADER_HEADER_SIZE] of Byte;
end;
var
GdiObject: PGdiObject;
procedure FillBitmapInfo(out Header: TBitmapHeader);
begin
FillChar(Header, SizeOf(Header), 0);
Header.InfoHeader.biSize := SizeOf(Header.InfoHeader);
Header.InfoHeader.biWidth := Width;
Header.InfoHeader.biHeight := Height;
Header.InfoHeader.biPlanes := Planes;
Header.InfoHeader.biBitCount := Bitcount;
Header.InfoHeader.biCompression := BI_RGB;
Header.InfoHeader.biSizeImage := (((BitCount * Width + 31) shr 5) shl 2) * Height;
Header.InfoHeader.biXPelsPerMeter := GetDeviceCaps(0, LOGPIXELSX);
Header.InfoHeader.biYPelsPerMeter := GetDeviceCaps(0, LOGPIXELSY);
Header.FileHeader.bfType := LeToN($4D42);
Header.FileHeader.bfSize := MIN_LOADER_HEADER_SIZE + Header.InfoHeader.biSizeImage;
Header.FileHeader.bfOffBits := MIN_LOADER_HEADER_SIZE;
end;
procedure LoadDataByPixbufLoader;
const
ALIGNDATA: Word = 0;
var
Header: TBitmapHeader;
Loader: PGdkPixbufLoader;
Src: PGDKPixbuf;
res: Boolean;
LineSize, Count: Integer;
BitsPtr: PByte;
begin
Loader := gdk_pixbuf_loader_new;
if Loader = nil then Exit;
FillBitmapInfo(Header);
Src := nil;
try
if not gdk_pixbuf_loader_write(Loader, TGdkPixBufBuffer(@Header), MIN_LOADER_HEADER_SIZE {$ifdef gtk2},nil{$endif})
then begin
DebugLn('WARNING: [TGtkWidgetSet.CreateBitmap] Error occured loading Bitmap Header!');
Exit;
end;
LineSize := (((BitCount * Width + 15) shr 4) shl 1);
if (LineSize and 2) <> 0
then begin
// bitmapdata needs to be DWord aligned, while CreateBitmap is Word aligned
// so "feed" the loader line by line :(
Count := Height;
res := True;
BitsPtr := BitmapBits;
while res and (Count > 0) do
begin
res := gdk_pixbuf_loader_write(Loader, TGdkPixBufBuffer(BitsPtr), LineSize {$ifdef gtk2},nil{$endif})
and gdk_pixbuf_loader_write(Loader, TGdkPixBufBuffer(@ALIGNDATA), 2 {$ifdef gtk2},nil{$endif});
Inc(BitsPtr, LineSize);
Dec(Count);
end;
end
else begin
// data is DWord aligned :)
res := gdk_pixbuf_loader_write(Loader, TGdkPixBufBuffer(BitmapBits), Header.InfoHeader.biSizeImage {$ifdef gtk2},nil{$endif});
end;
if not res
then begin
DebugLn('WARNING: [TGtkWidgetSet.CreateBitmap] Error occured loading Image!');
Exit;
end;
Src := gdk_pixbuf_loader_get_pixbuf(loader);
if Src = nil
then begin
DebugLn('WARNING: [TGtkWidgetSet.CreateBitmap] Error occured loading Pixbuf!');
Exit;
end;
finally
gdk_pixbuf_loader_close(Loader {$ifdef gtk2},nil {$endif});
end;
if GdiObject^.GDIPixmapObject.Image<>nil then
begin
gdk_pixmap_unref(GdiObject^.GDIPixmapObject.Image);
GdiObject^.GDIPixmapObject.Image:=nil;
end;
if GdiObject^.GDIPixmapObject.Mask<>nil then
begin
gdk_bitmap_unref(GdiObject^.GDIPixmapObject.Mask);
GdiObject^.GDIPixmapObject.Mask:=nil;
end;
gdk_pixbuf_render_pixmap_and_mask(Src,
GdiObject^.GDIPixmapObject.Image, GdiObject^.GDIPixmapObject.Mask, $80);
gdk_pixbuf_unref(Src);
GdiObject^.Depth := gdk_drawable_get_depth(GdiObject^.GDIPixmapObject.Image);
if GdiObject^.Depth = 1
then begin
if GdiObject^.GDIPixmapObject.Mask <> nil
then gdk_pixmap_unref(GdiObject^.GDIPixmapObject.Mask);
GdiObject^.GDIPixmapObject.Mask := nil;
GdiObject^.GDIBitmapType := gbBitmap;
end
else begin
GdiObject^.GDIBitmapType := gbPixmap;
end;
GdiObject^.Visual := gdk_window_get_visual(GDIObject^.GDIPixmapObject.Image);
if GdiObject^.Visual = nil
then GdiObject^.Visual := gdk_visual_get_best_with_depth(GdiObject^.Depth)
else gdk_visual_ref(GdiObject^.Visual);
GdiObject^.Colormap := gdk_colormap_new(GdiObject^.Visual, GdkTrue);
end;
procedure LoadBitmapData;
var
LineSize, n: Integer;
BitsPtr: Pointer;
Src, Dst: PByte;
begin
LineSize := (Width + 7) shr 3;
if (LineSize and 1) <> 0
then begin
// the gdk_bitmap_create_from_data expects data byte aligned while
// Createbitmap is word aligned. adjust data
BitsPtr := GetMem(LineSize * Height);
Dst := BitsPtr;
Src := BitmapBits;
for n := 1 to height do
begin
Move(Src^, Dst^, LineSize);
Inc(Src, LineSize + 1);
Inc(Dst, LineSize);
end;
end
else begin
BitsPtr := BitmapBits;
end;
GdiObject^.GDIBitmapType := gbBitmap;
GdiObject^.GDIBitmapObject := gdk_bitmap_create_from_data(nil, BitsPtr, Width, Height);
GdiObject^.Visual := nil; // bitmaps don't have a visual
GdiObject^.SystemVisual := False;
if BitsPtr <> BitmapBits
then FreeMem(BitsPtr);
end;
begin
//DebugLn(Format('Trace:> [TGtkWidgetSet.CreateBitmap] Width: %d, Height: %d, Planes: %d, BitCount: %d, BitmapBits: 0x%x', [Width, Height, Planes, BitCount, PtrUInt(BitmapBits)]));
if (BitCount < 1) or (Bitcount > 32)
then begin
Result := 0;
DebugLn(Format('ERROR: [TGtkWidgetSet.CreateBitmap] Illegal depth %d', [BitCount]));
Exit;
end;
GdiObject := NewGDIObject(gdiBitmap);
if BitmapBits = nil
then begin
if BitCount = 1
then begin
GdiObject^.GDIBitmapType := gbBitmap;
GdiObject^.GDIBitmapObject := gdk_pixmap_new(nil, Width, Height, 1);
GdiObject^.Visual := nil; // bitmaps don't have a visual
end
else begin
GdiObject^.GDIBitmapType := gbPixmap;
GdiObject^.GDIPixmapObject.Image := gdk_pixmap_new(nil, Width, Height, BitCount);
GdiObject^.Visual := gdk_window_get_visual(GdiObject^.GDIPixmapObject.Image);
gdk_visual_ref(GdiObject^.Visual);
end;
GdiObject^.SystemVisual := False;
end
else begin
if BitCount = 1
then begin
LoadBitmapData;
end
else begin
// Load the data by faking it as a windows bitmap stream (this handles all conversion)
// Problem with his method is that it doesn't result in the bitmap requested.
// it is always a device compatible bitmap
// maybe we should add a gdPixBuf type the the GDIObject for formats not compatible
// with a native pixmap format
LoadDataByPixbufLoader;
end;
end;
Result := HBITMAP(PtrUInt(GdiObject));
//DebugLn(Format('Trace:< [TGtkWidgetSet.CreateBitmap] --> 0x%x', [Integer(Result)]));
end;
{------------------------------------------------------------------------------
Function: CreateBrushIndirect
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.CreateBrushIndirect(const LogBrush: TLogBrush): HBRUSH;
const
HATCH_BDIAGONAL : array[0..7] of Byte = ($80, $40, $20, $10, $08, $04, $02, $01);
HATCH_CROSS : array[0..7] of Byte = ($08, $08, $08, $FF, $08, $08, $08, $08);
HATCH_DIAGCROSS : array[0..7] of Byte = ($81, $42, $24, $18, $18, $24, $42, $81);
HATCH_FDIAGONAL : array[0..7] of Byte = ($01, $02, $04, $08, $10, $20, $40, $80);
HATCH_HORIZONTAL: array[0..7] of Byte = ($00, $00, $00, $FF, $00, $00, $00, $00);
HATCH_VERTICAL : array[0..7] of Byte = ($08, $08, $08, $08, $08, $08, $08, $08);
var
GObject: PGdiObject;
TmpMask: PGdkBitmap;
begin
//DebugLn(Format('Trace:> [TGtkWidgetSet.CreateBrushIndirect] Style: %d, Color: %8x', [LogBrush.lbStyle, LogBrush.lbColor]));
{$IFDEF DebugGDKTraps}
BeginGDKErrorTrap;
{$ENDIF}
GObject := NewGDIObject(gdiBrush);
try
{$IFDEF DebugGDIBrush}
DebugLn('[TGtkWidgetSet.CreateBrushIndirect] ',DbgS(GObject));
{$ENDIF}
GObject^.IsNullBrush := False;
with LogBrush do
begin
case lbStyle of
BS_NULL {BS_HOLLOW}: // Same as BS_HOLLOW.
GObject^.IsNullBrush := True;
BS_SOLID: // Solid brush.
GObject^.GDIBrushFill := GDK_SOLID;
BS_HATCHED: // Hatched brush.
begin
GObject^.GDIBrushFill := GDK_STIPPLED;
case lbHatch of
HS_BDIAGONAL:
GObject^.GDIBrushPixmap := gdk_bitmap_create_from_data(
nil, pgchar(@HATCH_BDIAGONAL[0]), 8, 8);
HS_CROSS:
GObject^.GDIBrushPixmap := gdk_bitmap_create_from_data(
nil, pgchar(@HATCH_CROSS[0]), 8, 8);
HS_DIAGCROSS:
GObject^.GDIBrushPixmap := gdk_bitmap_create_from_data(
nil, pgchar(@HATCH_DIAGCROSS[0]), 8, 8);
HS_FDIAGONAL:
GObject^.GDIBrushPixmap := gdk_bitmap_create_from_data(
nil, pgchar(@HATCH_FDIAGONAL[0]), 8, 8);
HS_HORIZONTAL:
GObject^.GDIBrushPixmap := gdk_bitmap_create_from_data(
nil, pgchar(@HATCH_HORIZONTAL[0]), 8, 8);
HS_VERTICAL:
GObject^.GDIBrushPixmap := gdk_bitmap_create_from_data(
nil, pgchar(@HATCH_VERTICAL[0]), 8, 8);
else
GObject^.GDIBrushFill := GDK_SOLID;
end;
end;
BS_DIBPATTERN, // A pattern brush defined by a device-independent
// bitmap (DIB) specification. If lbStyle is BS_DIBPATTERN, the
// lbHatch member contains a handle to a packed DIB.Windows 95:
// Creating brushes from bitmaps or DIBs larger than 8x8 pixels
// is not supported. If a larger bitmap is given, only a portion
// of the bitmap is used.
BS_DIBPATTERN8X8, // Same as BS_DIBPATTERN.
BS_DIBPATTERNPT, // A pattern brush defined by a device-independent
// bitmap (DIB) specification. If lbStyle is BS_DIBPATTERNPT, the
// lbHatch member contains a pointer to a packed DIB.
BS_PATTERN, // Pattern brush defined by a memory bitmap.
BS_PATTERN8X8: // Same as BS_PATTERN.
begin
GObject^.GDIBrushPixmap := nil;
if IsValidGDIObject(lbHatch) and (PGdiObject(lbHatch)^.GDIType = gdiBitmap) then
begin
case PGdiObject(lbHatch)^.GDIBitmapType of
gbBitmap:
begin
GObject^.GDIBrushPixmap := PGdiObject(lbHatch)^.GDIBitmapObject;
GObject^.GDIBrushFill := GDK_STIPPLED;
end;
gbPixmap:
begin
GObject^.GDIBrushPixmap := PGdiObject(lbHatch)^.GDIPixmapObject.Image;
GObject^.GDIBrushFill := GDK_TILED;
end;
gbPixbuf:
begin
GObject^.GDIBrushPixmap := nil;
TmpMask := nil;
gdk_pixbuf_render_pixmap_and_mask(PGdiObject(lbHatch)^.GDIPixbufObject,
GObject^.GDIBrushPixmap, TmpMask, $80);
gdk_pixmap_unref(TmpMask);
end;
else
begin
DebugLn('TGtkWidgetSet.CreateBrushIndirect: Unsupported GDIBitmapType')
end;
end
end
else
RaiseGDBException('unsupported bitmap');
if GObject^.GDIBrushPixmap <> nil then
gdk_pixmap_ref(GObject^.GDIBrushPixmap);
end;
else
RaiseGDBException(Format('unsupported Style %d',[lbStyle]));
end;
{$IFDEF DebugGDKTraps}
EndGDKErrorTrap;
{$ENDIF}
if not GObject^.IsNullBrush then
SetGDIColorRef(GObject^.GDIBrushColor, lbColor);
end;
Result := HBRUSH(PtrUInt(GObject));
except
Result:=0;
DisposeGDIObject(GObject);
DebugLn('TGtkWidgetSet.CreateBrushIndirect failed');
end;
//DebugLn(Format('Trace:< [TGtkWidgetSet.CreateBrushIndirect] Got --> %x', [Result]));
end;
{------------------------------------------------------------------------------
Function: CreateCaret
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.CreateCaret(Handle: HWND; Bitmap: hBitmap;
Width, Height: Integer): Boolean;
var
GTKObject: PGTKObject;
BMP: PGDKPixmap;
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.CreateCaret] Finish');
GTKObject := PGTKObject(Handle);
Result := GTKObject <> nil;
if Result then begin
if gtk_type_is_a(gtk_object_type(GTKObject), GTKAPIWidget_GetType)
then begin
if IsValidGDIObjectType(Bitmap, gdiBitmap) then
BMP := PGdiObject(Bitmap)^.GDIBitmapObject
else
BMP := nil;
GTKAPIWidget_CreateCaret(PGTKAPIWidget(GTKObject), Width, Height, BMP);
end
// else if // TODO: other widgettypes
else begin
Result := False;
end;
end
else begin
//DebugLn('Trace:WARNING: [TGtkWidgetSet.CreateCaret] Got null HWND');
end;
end;
{------------------------------------------------------------------------------
Function: CreateCompatibleBitmap
Params: DC:
Width:
Height:
Returns:
Creates a bitmap compatible with the specified device context.
------------------------------------------------------------------------------}
function TGtkWidgetSet.CreateCompatibleBitmap(DC: HDC; Width, Height: Integer): HBITMAP;
var
DevCtx: TGtkDeviceContext absolute DC;
GDIObject: PGdiObject;
Depth : Longint;
Drawable, DefDrawable: PGDkDrawable;
begin
//DebugLn(Format('Trace:> [TGtkWidgetSet.CreateCompatibleBitmap] DC: 0x%x, W: %d, H: %d', [DC, Width, Height]));
if IsValidDC(DC) and (DevCtx.Drawable <> nil)
then begin
DefDrawable := DevCtx.Drawable;
Depth := gdk_drawable_get_depth(DevCtx.Drawable);
end
else begin
DefDrawable := nil;
Depth := gdk_visual_get_system^.Depth;
end;
if (Depth < 1) or (Depth > 32)
then begin
Result := 0;
DebugLn(Format('ERROR: [TGtkWidgetSet.CreateCompatibleBitmap] Illegal depth %d', [Depth]));
Exit;
end;
GdiObject := NewGDIObject(gdiBitmap);
Drawable := gdk_pixmap_new(DefDrawable, Width, Height, Depth);
GdiObject^.Visual := gdk_window_get_visual(Drawable);
if Depth = 1
then begin
GdiObject^.GDIBitmapType := gbBitmap;
GdiObject^.GDIBitmapObject := Drawable;
end
else begin
GdiObject^.GDIBitmapType := gbPixmap;
GdiObject^.GDIPixmapObject.Image := Drawable;
end;
if GdiObject^.Visual = nil
then begin
GdiObject^.Visual := gdk_visual_get_best_with_depth(Depth);
if GdiObject^.Visual = nil
then GdiObject^.Visual := gdk_visual_get_system;
GdiObject^.SystemVisual := True;
end
else begin
gdk_visual_ref(GdiObject^.Visual);
GdiObject^.SystemVisual := False;
end;
GdiObject^.Colormap := gdk_colormap_new(GdiObject^.Visual, GdkTrue);
Result := HBITMAP(PtrUInt(GdiObject));
//DebugLn(Format('Trace:< [TGtkWidgetSet.CreateCompatibleBitmap] DC: 0x%x --> 0x%x', [DC, Result]));
end;
{------------------------------------------------------------------------------
Function: CreateCompatibleDC
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.CreateCompatibleDC(DC: HDC): HDC;
var
pNewDC: TGtkDeviceContext;
begin
Result := 0;
pNewDC := NewDC;
// do not copy
// In a compatible DC you have to select a bitmap into it
(*
if IsValidDC(DC) then
with TGtkDeviceContext(DC)^ do
begin
pNewDC^.hWnd := hWnd;
pNewDC^.Drawable := Drawable;
pNewDC^.GC := gdk_gc_new(Drawable);
end
else begin
// We can't do anything yet
// Wait till a bitmap get selected
end;
*)
with pNewDC do
begin
gdk_color_black(gdk_colormap_get_system, @CurrentTextColor.Color);
BuildColorRefFromGDKColor(CurrentTextColor);
gdk_color_white(gdk_colormap_get_system, @CurrentBackColor.Color);
BuildColorRefFromGDKColor(CurrentBackColor);
end;
{$IFDEF Gtk1}
pNewDC.GetFont;
pNewDC.GetBrush;
pNewDC.GetPen;
{$ENDIF}
Result := HDC(pNewDC);
//DebugLn(Format('trace: [TGtkWidgetSet.CreateCompatibleDC] DC: 0x%x --> 0x%x', [Integer(DC), Integer(Result)]));
end;
function TGtkWidgetSet.DestroyCursor(Handle: hCursor): Boolean;
begin
Result := Handle <> 0;
if Result then
gdk_cursor_destroy(PGdkCursor(Handle));
end;
function TGTKWidgetSet.DestroyIcon(Handle: HICON): Boolean;
begin
// todo: handle cursors here, but how to check whether it is a cursor or an icon?
Result := Handle <> 0;
if Result then
gdk_pixbuf_unref(PGdkPixbuf(Handle));
end;
function TGTKWidgetSet.DPtoLP(DC: HDC; var Points; Count: Integer): BOOL;
var
DevCtx: TGtkDeviceContext absolute DC;
P: PPoint;
begin
Result := False;
if not IsValidDC(DC) then Exit(False);
if not DevCtx.HasTransf then Exit(True);
P := @Points;
while Count > 0 do
begin
Dec(Count);
DevCtx.InvTransfPoint(P^.X, P^.Y);
Inc(P);
end;
Result := True;
end;
{------------------------------------------------------------------------------
Function: CreateFontIndirect
Params: const LogFont: TLogFont
Returns: HFONT
Creates a font GDIObject.
------------------------------------------------------------------------------}
function TGtkWidgetSet.CreateFontIndirect(const LogFont: TLogFont): HFONT;
begin
Result := CreateFontIndirectEx(LogFont,'');
end;
{------------------------------------------------------------------------------
Function: CreateFontIndirectEx
Params: const LogFont: TLogFont; const LongFontName: string
Returns: HFONT
Creates a font GDIObject.
------------------------------------------------------------------------------}
function TGtkWidgetSet.CreateFontIndirectEx(const LogFont: TLogFont;
const LongFontName: string): HFONT;
{$IfDef GTK2}
begin
DebugLn('ToDo: TGtkWidgetSet.CreateFontIndirectEx');
Result:=0;
end;
{$Else Gtk1}
{off $DEFINE VerboseFonts}
var
GdiObject: PGdiObject;
FontNameRegistry, Foundry, FamilyName, WeightName,
Slant, SetwidthName, AddStyleName, PixelSize,
PointSize, ResolutionX, ResolutionY, Spacing, AverageWidth,
CharSetRegistry, CharSetCoding: string;
n: Integer;
sn, cs: Float;
CachedFont: TGtkFontCacheDescriptor;
CharsetRec: PCharSetEncodingRec;
Weightlist: TStringlist;
CalcPixelSize: boolean;
function LoadFontXLFD(aXLFD: string): boolean;
var
Desc: TGtkFontCacheDescriptor;
begin
GdiObject^.GDIFontObject := gdk_font_load(PChar(aXLFD));
Result:=GdiObject^.GDIFontObject<>nil;
{$ifdef VerboseFonts}
DebugLn('LoadFontXLFD: Trying ',aXLFD,' Matched=',dbgs(Result));
{$endif}
if Result then begin
Desc:=FontCache.Add(GdiObject^.GDIFontObject,LogFont,LongFontName);
if Desc<>nil then
Desc.xlfd:=aXLFD;
end;
end;
function LoadFont: boolean;
var
S: string;
begin
S:=FontNameRegistry+'-'+Foundry+'-'+FamilyName+'-'+WeightName
+'-'+Slant+'-'+SetwidthName+'-'+AddStyleName+'-'+PixelSize
+'-'+PointSize+'-'+ResolutionX+'-'+ResolutionY+'-'+Spacing+'-'+AverageWidth
+'-'+CharSetRegistry+'-'+CharSetCoding;
{ MG: heaptrc gets corrupted heap using the construction below:
S := Format('%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s',
[FontNameRegistry, Foundry, FamilyName, WeightName,
Slant, SetwidthName, AddStyleName, PixelSize,
PointSize, ResolutionX, ResolutionY, Spacing, AverageWidth,
CharSetRegistry, CharSetCoding
]);}
//DebugLn(' Trying Font "',S,'"');
result := LoadFontXLFD(S);
end;
function LoadFontExCharset: boolean;
var
i: Integer;
aSlant, aSpacing,head, mid, tail: string;
begin
Result := False;
Head := FontNameRegistry+'-'+Foundry+'-'+FamilyName+'-';
Mid := '-'+SetwidthName+'-'+AddStyleName+'-'+PixelSize
+'-'+PointSize+'-'+ResolutionX+'-'+ResolutionY+'-';
Tail := '-'+AverageWidth+'-'+CharSetRegistry+'-'+CharSetCoding;
//debugln('LoadFontExCharset Head=',Head,' Tail=',Tail);
for i:=0 to WeightList.Count-1 do begin
aSlant := Slant;
repeat
aSpacing:=Spacing;
repeat
result := LoadFontXLFD(Head+WeightList[i]+'-'+aSlant+Mid+aSpacing+Tail);
if result then
exit;
if aSpacing = 'm' then
aSpacing := 'c'
else
break;
until false;
if aSlant='i' then
aSlant:='o'
else
break;
until false;
end;
//debugln('LoadFontExCharset END');
end;
function LoadFontEx: boolean;
var
j: integer;
begin
Result := false;
//debugln('LoadFontEx START CharSetRegistry=',CharSetRegistry);
if CharSetRegistry<>'*' then
result := LoadFontExCharset
else
for j:=0 to CharSetEncodingList.Count-1 do begin
CharSetRec := CharsetEncodingList[j];
if (CharsetRec = nil) or (CharSetRec^.CharSet<>LogFont.lfCharset) then
continue;
CharSetCoding := CharsetRec^.CharSetCod;
CharSetRegistry := CharSetRec^.CharSetReg;
result := LoadFontExCharset;
if result then
break;
end;
//debugln('LoadFontEx END');
end;
procedure LoadDefaultFont;
begin
ReleaseGdiObject(GdiObject);
GdiObject:=CreateDefaultFont;
{$IFDEF VerboseFonts}
DebugLn('TGtkWidgetSet.CreateFontIndirectEx.LoadDefaultFont');
{$ENDIF}
end;
function GetDefaultFontFamilyName: string;
begin
Result:=GetDefaultFontName;
if IsFontNameXLogicalFontDesc(Result) then
Result := ExtractXLFDItem(Result,2);
if Result='' then Result:='*';
end;
function ExtractXLFDItemMask(const ALongFontName: string;
Index: Integer): string;
begin
Result:=ExtractXLFDItem(ALongFontName,Index);
if Result='' then Result:='*';
end;
function FamilyNameExists: boolean;
var
AFont: PGdkFont;
S: String;
begin
//S := '*-*-'+FamilyName+'*-*-*-*-*-*-*-*-*-*-*-*-*';
S := '-'+Foundry+'-'+FamilyName+'-*-*-*-*-*-*-*-*-*-*-*-*';
AFont:=gdk_font_load(PChar(s));
Result:=AFont<>nil;
if Result then gdk_font_unref(AFont);
end;
function CheckFontNameIsMangledXLogicalFontDesc(const ALongFontName: string
): boolean;
var
c: Integer;
i: Integer;
begin
c:=0;
for i:=1 to length(ALongFontName) do
if ALongFontName[i]='-' then inc(c);
Result:=(c>5) and (c<>14);
if Result then
debugln('WARNING: Fontnamt "',ALongFontName,'" seems to be a XLFD fontname, but has 14<>',dbgs(c),' minus signs');
end;
function GetPixelSize(Offset: Integer): string;
begin
with LogFont do begin
result := IntToStr(Abs(lfHeight)+Offset);
{$IFNDEF OLD_ROTATION}
if lfOrientation <> 0 then begin
SinCos(lfOrientation/1800.*Pi, sn, cs);
cs := cs*(Abs(lfHeight)+Offset);
sn := sn*(Abs(lfHeight)+Offset);
Result := Format('[%.3f %.3f %.3f %.3f]', [cs, sn, -sn, cs]);
repeat
n := Pos('-', Result);
if n > 0 then
Result[n] := '~';
until n <= 0;
end;
end;
{$ENDIF}
end;
begin
// For info about xlfd see:
// http://wwwinfo.cern.ch/umtf/working-groups/X11/fonts/hp_xlfd.html
// Lets fill in all the xlfd parts. Assume we have scalable fonts.
{$IFDEF VerboseFonts}
DebugLn('TGtkWidgetSet.CreateFontIndirectEx A Name=',LogFont.lfFaceName,' Height=',dbgs(LogFont.lfHeight),' LongName=',LongFontName);
{$ENDIF}
Result := 0;
GDIObject := NewGDIObject(gdiFont);
try
GdiObject^.UntransfFontHeight := 0;
GdiObject^.LogFont := LogFont;
CachedFont:=FontCache.FindGTkFontDesc(LogFont,LongFontName);
if CachedFont<>nil then begin
CachedFont.Item.IncreaseRefCount;
GdiObject^.GDIFontObject := TGtkFontCacheItem(CachedFont.Item).GtkFont;
{$IFDEF VerboseFonts}
WriteLn('Was in cache: ', Integer(CachedFont));
{$ENDIF}
exit;
end;
// set default values
FontNameRegistry := '*';
Foundry := '*';
FamilyName := '*';
WeightName := '*';
Slant := '*';
SetwidthName := '*';
AddStyleName := '*';
PixelSize := '*';
PointSize := '*';
ResolutionX := '*';
ResolutionY := '*';
Spacing := '*';
AverageWidth := '*';
CharSetRegistry := '*';
CharSetCoding := '*';
// check if LongFontName is in XLFD format and get nicer defaults
// This way, the user can set X fonts that are not supported by TFont.
{$IFDEF VerboseFonts}
DebugLn('TGtkWidgetSet.CreateFontIndirectEx Name="',LogFont.lfFaceName,'"',
' Long="',LongFontName,'" IsXLFD=',dbgs(IsFontNameXLogicalFontDesc(LongFontName))
,' ',dbgs(ord(LogFont.lfFaceName[0])));
{$ENDIF}
if IsFontNameXLogicalFontDesc(LongFontName) then begin
FontNameRegistry := ExtractXLFDItemMask(LongFontName,0);
Foundry := ExtractXLFDItemMask(LongFontName,1);
FamilyName := ExtractXLFDItemMask(LongFontName,2);
WeightName := ExtractXLFDItemMask(LongFontName,3);
Slant := ExtractXLFDItemMask(LongFontName,4);
SetWidthName := ExtractXLFDItemMask(LongFontName,5);
AddStyleName := ExtractXLFDItemMask(LongFontName,6);
PixelSize := ExtractXLFDItemMask(LongFontName,7);
PointSize := ExtractXLFDItemMask(LongFontName,8);
ResolutionX := ExtractXLFDItemMask(LongFontName,9);
ResolutionY := ExtractXLFDItemMask(LongFontName,10);
Spacing := ExtractXLFDItemMask(LongFontName,11);
AverageWidth := ExtractXLFDItemMask(LongFontName,12);
CharSetRegistry := ExtractXLFDItemMask(LongFontName,13);
CharSetCoding := ExtractXLFDItemMask(LongFontName,14);
end else if CheckFontNameIsMangledXLogicalFontDesc(LongFontName) then begin
// warned
end;
with LogFont do
begin
if lfFaceName[0] = #0
then begin
//DebugLn('ERROR: [TGtkWidgetSet.CreateFontIndirectEx] No fontname');
Exit;
end;
FamilyName := StrPas(lfFaceName);
if (CompareText(FamilyName,'default')<>0) then begin
// check if we have foundry encoded in family name
n := pos(FOUNDRYCHAR_OPEN, FamilyName);
if n<>0 then begin
Foundry := copy(FamilyName, n+1, Length(FamilyName));
familyName := trim(copy(familyName, 1, n-1));
n := pos(FOUNDRYCHAR_CLOSE, Foundry);
if n<>0 then
Delete(Foundry, n, Length(Foundry));
end;
if not FamilyNameExists then
FamilyName:='default';
end;
if CompareText(FamilyName,'default')=0 then begin
{$IFDEF VerboseFonts}
DebugLn('TGtkWidgetSet.CreateFontIndirectEx FamilyName="',FamilyName,'" PixelSize=',PixelSize,' LogFont.lfHeight=',dbgs(LogFont.lfHeight));
{$ENDIF}
if (LogFont.lfHeight=0) then begin
LoadDefaultFont;
exit;
end else begin
FamilyName:=GetDefaultFontFamilyName;
Foundry:='*';
end;
end;
//DebugLn(Format('trace: [TGtkWidgetSet.CreateFontIndirectEx] Name: %s, Height: %d', [FamilyName, lfHeight]));
// calculate weight offset.
// API XLFD
// --------------------- --------------
// Weight=400 --> normal normal
// Weight=700 --> bold normal+4000 (or bold in non scalable fonts)
//
// So in API the offset for normal = 400 and an increase of 300 equals to
// an offset of 4000
if WeightName='*' then begin
case lfWeight of
FW_DONTCARE : WeightName := '*';
FW_LIGHT : WeightName := 'light';
FW_NORMAL : ; // try several later
FW_MEDIUM : WeightName := 'medium';
FW_SEMIBOLD : WeightName := 'demi bold';
FW_BOLD : ; // try several later
else begin
n := ((lfWeight - FW_NORMAL) * 4000) div (FW_BOLD - FW_NORMAL);
if n = 0
then WeightName := 'normal'
else if n > 0
then WeightName := Format('normal+%d', [n])
else WeightName := Format('normal%d', [n]);
end;
end;
end;
if Slant='*' then begin
// TODO: find out if escapement has something to do with slant
if lfItalic = 0 then Slant := 'r' else Slant := 'i';
end;
// SetWidthName := '*';
{$IFDEF OLD_ROTATION}
if AddStyleName='*' then begin
// calculate Style name extentions (=rotation)
// API XLFD
// --------------------- --------------
// Orientation 1/10 deg 1/64 deg
if lfOrientation = 0
then AddStyleName := '*'
else begin
n := (lfOrientation * 64) div 10;
if n >= 0
then AddStyleName := Format('+%d', [n])
else AddStyleName := Format('+%d', [n]);
end;
end;
{$ENDIF}
CalcPixelSize:= (PixelSize='*') and (PointSize='*');
if CalcPixelSize then begin
// TODO: make more accurate (implement the meaning of
// positive and negative height values.
PixelSize := GetPixelSize(0);
// Since we use pixelsize, it isn't allowed to give a value here
PointSize := '*';
// Use the default
ResolutionX := '*';
ResolutionY := '*';
end;
if Spacing='*' then begin
// spacing
if (FIXED_PITCH and lfPitchAndFamily)>0 then
Spacing := 'm' // mono spaced
else if (VARIABLE_PITCH and lfPitchAndFamily)>0 then
Spacing := 'p' // proportional spaced
else
Spacing := '*';
end;
if AverageWidth='*' then begin
// calculate AverageWidth
// API XLFD
// --------------------- --------------
// Width pixel 1/10 pixel
if lfWidth = 0
then AverageWidth := '*'
else AverageWidth := InttoStr(lfWidth * 10);
end;
// this section tries several combinations of charset-weightname-slant
//
WeightList := TStringList.Create;
if LogFOnt.LfWeight = FW_BOLD then
// bold appears most times
WeightList.CommaText := 'bold,semibold,demibold,black,*'
else
// medium appears most times but if there is normal, use it
WeightList.CommaText := 'normal,medium,regular,light,*';
if WeightName<>'*' then
WeightList.Insert(0, WeightName);
try
if LoadFontEx then
exit;
// not found yet, before doing a generic fall over
// try to do a more specific guess.
if CalcPixelSize then
repeat
// try one pixel smaller
{$IFDEF VerboseFonts}
debugln('TGtkWidgetSet.CreateFontIndirectEx, LoadFontEx: try one pixel smaller');
{$ENDIF}
PixelSize:=GetPixelSize(-1);
if LoadFontEx then
exit;
// try one pixel bigger
{$IFDEF VerboseFonts}
debugln('TGtkWidgetSet.CreateFontIndirectEx, LoadFontEx: try one pixel bigger');
{$ENDIF}
PixelSize:=GetPixelSize(1); // try
if LoadFontEx then
exit;
// not found yet
// if font was slanted try with any within font face.
if Slant<>'*' then begin
Slant := '*';
continue;
end;
break;
until false;
finally
WeightList.Free;
end;
end;
// next checks are fall over
{$IFDEF VerboseFonts}
debugln('TGtkWidgetSet.CreateFontIndirectEx ');
{$ENDIF}
{
if LoadFont then exit;
// try all weights
WeightName := '*';
if LoadFont then exit;
}
// try one height smaller
{$IFDEF VerboseFonts}
debugln('TGtkWidgetSet.CreateFontIndirectEx try one height smaller');
{$ENDIF}
PixelSize := IntToStr(Abs(LogFont.lfHeight)-1);
// Since we use pixelsize, it isn't allowed to give a value here
PointSize := '*';
// Use the default
ResolutionX := '*';
ResolutionY := '*';
if LoadFont then exit;
// try one height bigger
{$IFDEF VerboseFonts}
debugln('TGtkWidgetSet.CreateFontIndirectEx try one height bigger');
{$ENDIF}
PixelSize := IntToStr(Abs(LogFont.lfHeight)+1);
if LoadFont then exit;
PixelSize := IntToStr(Abs(LogFont.lfHeight));
// try instead of mono spaced -> character cell spaced
if (Spacing='m') then begin
{$IFDEF VerboseFonts}
debugln('TGtkWidgetSet.CreateFontIndirectEx try instead of mono spaced -> character cell spaced');
{$ENDIF}
Spacing:='c';
if LoadFont then exit;
end;
{
// try instead of italic -> oblique
if (Slant='i') then begin
Slant := 'o';
if LoadFont then exit;
end;
// try all slants
Slant := '*';
if LoadFont then exit;
}
// try all spacings
if spacing<>'*' then begin
{$IFDEF VerboseFonts}
debugln('TGtkWidgetSet.CreateFontIndirectEx try all spacings');
{$ENDIF}
Spacing := '*';
if LoadFont then exit;
end;
if charSetCoding<>'*' then begin
{$IFDEF VerboseFonts}
debugln('TGtkWidgetSet.CreateFontIndirectEx try all charsets');
{$ENDIF}
charsetCoding := '*';
charSetRegistry:= '*';
if LoadFont then exit;
end;
if (Foundry<>'*') then begin
// try all Families
{$IFDEF VerboseFonts}
debugln('TGtkWidgetSet.CreateFontIndirectEx try all families');
{$ENDIF}
PixelSize := IntToStr(Abs(LogFont.lfHeight));
FamilyName := '*';
if LoadFont then exit;
end;
// nothing exists -> use default
LoadDefaultFont;
finally
if GdiObject^.GDIFontObject = nil
then begin
{$IFDEF VerboseFonts}
DebugLn('[TGtkWidgetSet.CreateFontIndirect] ',DbgS(GdiObject),' ',dbgs(FGDIObjects.Count));
{$ENDIF}
DisposeGDIObject(GdiObject);
Result := 0;
end
else begin
Result := HFONT(PtrUInt(GdiObject));
end;
if Result = 0
then
DebugLn('WARNING: [TGtkWidgetSet.CreateFontIndirectEx] NOT found XLFD: <'+LongFontName+'> Fontname="'+LogFont.lfFaceName+'"')
else begin
//DebugLn(Format('Trace: [TGtkWidgetSet.CreateFontIndirectEx] found XLFD: <%s>', [LongFontName]));
end;
end;
end;
{$EndIf}
function TGTKWidgetSet.CreateIconIndirect(IconInfo: PIconInfo): HICON;
procedure GetColorMask(AImage, AMask: PGDKPixmap; AImgBits, AMskBits: PByte; AWidth, AHeight: integer);
var
i, j: integer;
colormap: PGDKColormap;
Image, MaskImage: PGDKImage;
GDKColor: TGDKColor;
Pixel, MaskPixel: LongWord;
offset: byte;
procedure SetColorAndMaskPixmap(c: TGdkColor; MaskPixel: LongWord);
var
c_bit, m_bit: byte;
begin
// c_bit := Ord(0.222 * c.red + 0.707 * c.green + 0.071 * c.blue >= $8000);
// do some int math
c_bit := Ord(cardinal(222) * c.red
+ cardinal(707) * c.green
+ cardinal(071) * c.blue
>= $8000 * 1000);
m_bit := ord(MaskPixel = 1);
AImgBits^ := AImgBits^ or (c_bit shl offset);
AMskBits^ := AMskBits^ or (m_bit shl offset);
inc(offset);
if offset > 7 then
begin
inc(AImgBits);
inc(AMskBits);
offset := 0;
end;
end;
procedure SetColorAndMaskBitmap(ColorPixel, MaskPixel: LongWord);
begin
AImgBits^ := AImgBits^ or (ColorPixel shl offset);
AMskBits^ := AMskBits^ or (MaskPixel shl offset);
inc(offset);
if offset > 7 then
begin
inc(AImgBits);
inc(AMskBits);
offset := 0;
end;
end;
begin
// most of this code was taken from TGtkWidgetSet.DCGetPixel
Image := gdk_drawable_get_image(AImage, 0, 0, AWidth, AHeight);
if AMask = nil
then MaskImage := nil
else MaskImage := gdk_drawable_get_image(AMask, 0, 0, AWidth, AHeight);
offset := 0;
if gdk_drawable_get_depth(AImage) = 1 then
begin
for j := 0 to AHeight - 1 do
for i := 0 to AWidth - 1 do
begin
Pixel := gdk_image_get_pixel(Image, i, j);
if MaskImage = nil
then MaskPixel := 1
else MaskPixel := gdk_image_get_pixel(MaskImage, i, j);
SetColorAndMaskBitmap(Pixel, MaskPixel);
end;
end
else
begin
{$ifdef Gtk1}
// previously gdk_image_get_colormap(image) was used, implementation
// was casting GdkImage to GdkWindow which is not valid and cause AVs
if gdk_window_get_type(PGdkWindow(AImage))= GDK_WINDOW_PIXMAP then
colormap := nil // pixmaps are created with null colormap, get system one instead
else
colormap := gdk_window_get_colormap(PGdkWindow(AImage));
{$else}
colormap := gdk_image_get_colormap(image);
{$endif}
if colormap = nil then
colormap := gdk_colormap_get_system;
for j := 0 to AHeight - 1 do
for i := 0 to AWidth - 1 do
begin
Pixel := gdk_image_get_pixel(Image, i, j);
if MaskImage = nil
then MaskPixel := 1
else MaskPixel := gdk_image_get_pixel(MaskImage, i, j);
FillChar(GDKColor,SizeOf(GDKColor), 0);
gdk_colormap_query_color(colormap, Pixel, @GDKColor);
SetColorAndMaskPixmap(GDKColor, MaskPixel);
end;
end;
gdk_image_unref(Image);
if MaskImage <> nil
then gdk_image_unref(MaskImage);
end;
var
FG, BG: TGDKColor;
Img, Msk: PGdkPixmap;
Pixbuf: PGdkPixbuf;
srcbitmap, mskbitmap: PGdkBitmap;
W, H, bitlen: integer;
ImgBits, MskBits: array of byte;
begin
Result := 0;
if not IsValidGDIObject(IconInfo^.hbmColor) then Exit;
if PGDIObject(IconInfo^.hbmColor)^.GDIBitmapType = gbPixbuf then
begin
Pixbuf := PGDIObject(IconInfo^.hbmColor)^.GDIPixbufObject;
if IconInfo^.fIcon then
begin
// Creating PixBuf from pixmap and mask
Result := HICON(PtrUInt(gdk_pixbuf_copy(pixbuf)));
Exit;
end;
W := gdk_pixbuf_get_width(Pixbuf);
H := gdk_pixbuf_get_height(Pixbuf);
Img := nil;
Msk := nil;
gdk_pixbuf_render_pixmap_and_mask(Pixbuf, Img, Msk, $80);
end
else
begin
Img := PGDIObject(IconInfo^.hbmColor)^.GDIBitmapObject;
gdk_drawable_get_size(Img, @W, @H);
Msk := CreateGdkMaskBitmap(IconInfo^.hbmColor, IconInfo^.hbmMask);
//DbgDumpPixmap(Img, 'Image');
//DbgDumpPixmap(Msk, 'Mask');
if IconInfo^.fIcon then
begin
// Creating PixBuf from pixmap and mask
Result := HICON(PtrUInt(CreatePixbufFromImageAndMask(Img, 0, 0, W, H, nil, Msk)));
if Msk <> nil then
gdk_bitmap_unref(Msk);
Exit;
end;
end;
try
// Create cursor
bitlen := (W * H) shr 3;
SetLength(ImgBits, bitlen);
SetLength(MskBits, bitlen);
FillChar(ImgBits[0], bitlen, 0);
FillChar(MskBits[0], bitlen, 0);
GetColorMask(Img, Msk, @ImgBits[0], @MskBits[0], W, H);
srcbitmap := gdk_bitmap_create_from_data(nil, @ImgBits[0], W, H);
mskbitmap := gdk_bitmap_create_from_data(nil, @MskBits[0], W, H);
// white
fg.red := $FFFF;
fg.green := $FFFF;
fg.blue := $FFFF;
fg.pixel := 0;
// black
bg.red := 0;
bg.green := 0;
bg.blue := 0;
bg.pixel := 0;
Result := HCURSOR(PtrUInt(gdk_cursor_new_from_pixmap(srcbitmap, mskbitmap,
@fg, @bg, IconInfo^.xHotspot, IconInfo^.yHotspot)));
gdk_pixmap_unref(srcbitmap);
gdk_pixmap_unref(mskbitmap);
finally
if msk <> nil
then gdk_bitmap_unref(msk);
if Img <> PGDIObject(IconInfo^.hbmColor)^.GDIBitmapObject
then gdk_pixmap_unref(Img);
end;
end;
{------------------------------------------------------------------------------
Function: CreatePalette
Params: LogPalette
Returns: a handle to the Palette created
------------------------------------------------------------------------------}
function TGtkWidgetSet.CreatePalette(const LogPalette: TLogPalette): HPALETTE;
var
GObject: PGdiObject;
begin
//DebugLn('trace:[TGtkWidgetSet.CreatePalette]');
GObject := NewGDIObject(gdiPalette);
GObject^.SystemPalette := False;
GObject^.PaletteRealized := False;
GObject^.VisualType := GDK_VISUAL_PSEUDO_COLOR;
GObject^.PaletteVisual := nil;
{$IFDEF DebugGDKTraps}
BeginGDKErrorTrap;
{$ENDIF}
GObject^.PaletteVisual := gdk_visual_get_best_with_type(GObject^.VisualType);
if GObject^.PaletteVisual = nil
then begin
GObject^.PaletteVisual := GDK_Visual_Get_System;
GDK_Visual_Ref(GObject^.PaletteVisual);
end;
GObject^.PaletteColormap := GDK_Colormap_new(GObject^.PaletteVisual, GdkTrue);
{$IFDEF DebugGDKTraps}
EndGDKErrorTrap;
{$ENDIF}
GObject^.RGBTable := TDynHashArray.Create(-1);
GObject^.RGBTable.OnGetKeyForHashItem:=@GetRGBAsKey;
GObject^.IndexTable := TDynHashArray.Create(-1);
GObject^.IndexTable.OnGetKeyForHashItem:=@GetIndexAsKey;
InitializePalette(GObject, LogPalette.palPalEntry, LogPalette.palNumEntries);
Result := HPALETTE(PtrUInt(GObject));
end;
{------------------------------------------------------------------------------
Function: CreatePenIndirect
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.CreatePenIndirect(const LogPen: TLogPen): HPEN;
var
GObject: PGdiObject;
begin
//DebugLn('trace:[TGtkWidgetSet.CreatePenIndirect]');
//write('CreatePenIndirect->');
GObject := NewGDIObject(gdiPen);
GObject^.UnTransfPenWidth := 0;
GObject^.GDIPenDashes := nil;
GObject^.IsExtPen := False;
with LogPen do
begin
GObject^.GDIPenStyle := lopnStyle;
GObject^.GDIPenWidth := lopnWidth.X;
SetGDIColorRef(GObject^.GDIPenColor,lopnColor);
end;
Result := HPEN(PtrUInt(GObject));
end;
{------------------------------------------------------------------------------
Method: CreatePolygonRgn
Params: Points, NumPts, FillMode
Returns: the handle to the region
Creates a Polygon, a closed many-sided shaped region. The Points parameter is
an array of points that give the vertices of the polygon. FillMode=Winding
determines what points are going to be included in the region. When Winding
is True, points are selected by using the Winding fill algorithm. When Winding
is False, points are selected by using using the even-odd (alternative) fill
algorithm. NumPts indicates the number of points to use.
The first point is always connected to the last point.
------------------------------------------------------------------------------}
function TGtkWidgetSet.CreatePolygonRgn(Points: PPoint; NumPts: Integer;
FillMode: integer): HRGN;
var
i: integer;
PointArray: PGDKPoint;
GObject: PGdiObject;
fr : TGDKFillRule;
begin
Result := 0;
if NumPts<=0 then exit;
GObject := NewGDIObject(gdiRegion);
GetMem(PointArray,SizeOf(TGdkPoint)*NumPts);
for i:=0 to NumPts-1 do begin
PointArray[i].x:=Points[i].x;
PointArray[i].y:=Points[i].y;
end;
If FillMode=Winding then
fr := GDK_WINDING_RULE
else
fr := GDK_EVEN_ODD_RULE;
GObject^.GDIRegionObject := gdk_region_polygon(PointArray, NumPts, fr);
FreeMem(PointArray);
Result := HRGN(PtrUInt(GObject));
end;
{------------------------------------------------------------------------------
Function: CreateRectRgn
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.CreateRectRgn(X1,Y1,X2,Y2 : Integer): HRGN;
var
R: TGDKRectangle;
RRGN: PGDKRegion;
GObject: PGdiObject;
RegionObj: PGdkRegion;
begin
GObject := NewGDIObject(gdiRegion);
if X1<=X2 then begin
R.X := gint16(X1);
R.Width := X2 - X1;
end else begin
R.X := gint16(X2);
R.Width := X1 - X2;
end;
if Y1<=Y2 then begin
R.Y := gint16(Y1);
R.Height := Y2 - Y1;
end else begin
R.Y := gint16(Y2);
R.Height := Y1 - Y1;
end;
RRGN := gdk_region_new;
RegionObj:=PGdkRegion(gdk_region_union_with_rect(RRGN,@R));
GObject^.GDIRegionObject := RegionObj;
gdk_region_destroy(RRGN);
Result := HRGN(PtrUInt(GObject));
//DebugLn('TGtkWidgetSet.CreateRectRgn A ',GDKRegionAsString(RegionObj));
end;
{------------------------------------------------------------------------------
Function: CombineRgn
Params: Dest, Src1, Src2, fnCombineMode
Returns: longint
Combine the 2 Source Regions into the Destination Region using the specified
Combine Mode. The Destination must already be initialized. The Return value
is the Destination's Region type, or ERROR.
The Combine Mode can be one of the following:
RGN_AND : Gets a region of all points which are in both source regions
RGN_COPY : Gets an exact copy of the first source region
RGN_DIFF : Gets a region of all points which are in the first source
region but not in the second.(Source1 - Source2)
RGN_OR : Gets a region of all points which are in either the first
source region or in the second.(Source1 + Source2)
RGN_XOR : Gets all points which are in either the first Source Region
or in the second, but not in both.
The result can be one of the following constants
Error
NullRegion
SimpleRegion
ComplexRegion
------------------------------------------------------------------------------}
function TGtkWidgetSet.CombineRgn(Dest, Src1, Src2 : HRGN;
fnCombineMode : Longint) : Longint;
var
Continue : Boolean;
D, S1, S2 : PGDKRegion;
DObj, S1Obj, S2Obj : PGDIObject;
begin
Result := SIMPLEREGION;
DObj := PGdiObject(Dest);
S1Obj := PGdiObject(Src1);
S2Obj := PGdiObject(Src2);
Continue := IsValidGDIObject(Dest) and IsValidGDIObject(Src1)
and IsValidGDIObject(Src2);
If Not Continue then begin
DebugLn('WARNING: [TGtkWidgetSet.CombineRgn] Invalid HRGN');
Result := Error;
end
else begin
S1 := S1Obj^.GDIRegionObject;
S2 := S2Obj^.GDIRegionObject;
//DebugLn('TGtkWidgetSet.CombineRgn A fnCombineMode=',Dbgs(fnCombineMode));
Case fnCombineMode of
RGN_AND :
D := PGDKRegion(gdk_region_intersect(S1, S2));
RGN_COPY :
D := gdk_region_copy(S1);
RGN_DIFF :
D := PGDKRegion(gdk_region_subtract(S1, S2));
RGN_OR :
D := PGDKRegion(gdk_region_union(S1, S2));
RGN_XOR :
D := PGDKRegion(gdk_region_xor(S1, S2));
else begin
Result:= ERROR;
D := nil;
end;
end;
if DObj^.GDIRegionObject <> nil then
gdk_region_destroy(DObj^.GDIRegionObject);
DObj^.GDIRegionObject := D;
Result := RegionType(D);
//DebugLn('TGtkWidgetSet.CombineRgn B Mode=',dbgs(fnCombineMode),
// ' S1=',GDKRegionAsString(S1),' S2=',GDKRegionAsString(S2),' D=',GDKRegionAsString(D),'');
end;
end;
{------------------------------------------------------------------------------
Function: DeleteDC
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.DeleteDC(hDC: HDC): Boolean;
begin
// TODO:
// for now it's just the same, however CreateDC/FreeDC
// and GetDC/ReleaseDC are couples
// we should use gdk_new_gc for create and gtk_new_gc for Get
Result:= (ReleaseDC(0, hDC) = 1);
end;
{------------------------------------------------------------------------------
Function: DeleteObject
Params: none
Returns: Nothing
DeleteObject is allowed while the object is still selected. The msdn docs
are misleading. Marc tested with resource profiler under win XP.
------------------------------------------------------------------------------}
function TGtkWidgetSet.DeleteObject(GDIObject: HGDIOBJ): Boolean;
procedure RaiseInvalidGDIObject;
begin
{$ifdef TraceGdiCalls}
DebugLn();
DebugLn('TGtkWidgetSet.DeleteObject: TraceCall for invalid object: ');
DumpBackTrace(PgdiObject(GdiObject)^.StackAddrs);
DebugLn();
DebugLn('Exception will follow:');
DebugLn();
{$endif}
RaiseGDBException('TGtkWidgetSet.DeleteObject invalid GdiObject='+dbgs(GdiObject));
end;
var
GDIObjectExists: boolean;
begin
if GDIObject = 0 then
begin
Result := True;
Exit;
end;
{$IFDEF DebugLCLComponents}
if DebugGdiObjects.IsDestroyed(GDIObject) then
begin
DebugLn(['TGtkWidgetSet.DeleteObject object already deleted ',GDIObject]);
debugln(DebugGdiObjects.GetInfo(PGdiObject(GDIObject),true));
Halt;
end;
{$ENDIF}
// Find out if we want to release internal GDI object
GDIObjectExists := FGDIObjects.Contains(PGdiObject(GDIObject));
Result := GDIObjectExists;
if not GDIObjectExists then
begin
RaiseInvalidGDIObject;
end;
Result := ReleaseGDIObject(PGdiObject(GDIObject));
end;
{------------------------------------------------------------------------------
Function: DestroyCaret
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.DestroyCaret(Handle: HWND): Boolean;
var
GTKObject: PGTKObject;
begin
GTKObject := PGTKObject(Handle);
Result := true;
if GTKObject<>nil then begin
if gtk_type_is_a(gtk_object_type(GTKObject), GTKAPIWidget_GetType)
then begin
GTKAPIWidget_DestroyCaret(PGTKAPIWidget(GTKObject));
end
// else if // TODO: other widgettypes
else begin
Result := False;
end;
end
else begin
//DebugLn('Trace:WARNING: [TGtkWidgetSet.DestroyCaret] Got null HWND');
end;
end;
{------------------------------------------------------------------------------
Function: DrawFrameControl
Params:
Returns:
------------------------------------------------------------------------------}
function TGtkWidgetSet.DrawFrameControl(DC: HDC; const Rect : TRect;
uType, uState : Cardinal) : Boolean;
{const
ADJUST_FLAG: array[Boolean] of Integer = (0, BF_ADJUST);
PUSH_EDGE_FLAG: array[Boolean] of Integer = (EDGE_RAISED, EDGE_SUNKEN);
PUSH_EDGE_FLAG2: array[Boolean] of Integer = (0, BF_FLAT);}
var
DevCtx: TGtkDeviceContext absolute DC;
Widget: PGtkWidget;
R: TRect;
procedure DrawButtonPush;
var
State: TGtkStateType;
Shadow: TGtkShadowType;
aStyle : PGTKStyle;
aDC: TGtkDeviceContext;
DCOrigin: TPoint;
begin
//if Widget<>nil then begin
// use the gtk paint functions to draw a widget style dependent button
//writeln('DrawButtonPush ',
// ' DFCS_BUTTONPUSH=',uState and DFCS_BUTTONPUSH,
// ' DFCS_PUSHED=',uState and DFCS_PUSHED,
// ' DFCS_INACTIVE=',uState and DFCS_INACTIVE,
// ' DFCS_FLAT=',uState and DFCS_FLAT,
// '');
// set State (the interior filling style)
if (DFCS_PUSHED and uState)<>0 then
State := GTK_STATE_ACTIVE //button pressed(GTK ignores disabled)
else if (DFCS_INACTIVE and uState)<>0 then
State := GTK_STATE_INSENSITIVE //button disabled
else if (DFCS_HOT and uState)<>0 then
State := GTK_STATE_PRELIGHT // button enabled, special (e.g. mouse over)
else
State := GTK_STATE_NORMAL; // button enabled, normal
// set Shadow (the border style)
if (DFCS_PUSHED and uState)<>0 then begin
// button down
Shadow:=GTK_SHADOW_IN;
end else begin
if (((DFCS_FLAT+DFCS_CHECKED) and uState)=DFCS_FLAT) then begin
// button up, flat, no special
Shadow:=GTK_SHADOW_ETCHED_OUT;
//Shadow:=GTK_SHADOW_NONE;
end else begin
// button up
Shadow:=GTK_SHADOW_OUT;
end;
end;
aDC:=TGtkDeviceContext(DC);
DCOrigin:= aDC.Offset;
If Widget <> nil then
aStyle := gtk_widget_get_style(Widget)
else
aStyle := GetStyle(lgsButton);
If aStyle = nil then
aStyle := GetStyle(lgsGTK_Default);
// MG: You can't assign a style to any window. Why it is needed anyway?
//aStyle := gtk_style_attach(gtk_style_ref(aStyle),aDC.Drawable);
if aStyle<>nil then begin
If (Shadow=GTK_SHADOW_NONE) then
gtk_paint_flat_box(aStyle,aDC.Drawable,
State,
Shadow,
nil,
GetStyleWidget(lgsButton),
'button',
R.Left+DCOrigin.X,R.Top+DCOrigin.Y,
R.Right-R.Left,R.Bottom-R.Top)
else
gtk_paint_box(aStyle,aDC.Drawable,
State,
Shadow,
nil,
GetStyleWidget(lgsButton),
'button',
R.Left+DCOrigin.X,R.Top+DCOrigin.Y,
R.Right-R.Left,R.Bottom-R.Top);
end;
Result := True;
end;
procedure DrawCheckOrRadioButton(IsRadioButton: Boolean);
const
LazGtkStyleMap: array[Boolean] of TLazGtkStyle = (lgsCheckbox, lgsRadiobutton);
var
State: TGtkStateType;
Shadow: TGtkShadowType;
aDC: TGtkDeviceContext;
DCOrigin: TPoint;
Style : PGTKStyle;
Widget : PGTKWidget;
begin
// use the gtk paint functions to draw a widget style dependent check/radio button
if (DFCS_BUTTON3STATE and uState)<>0 then
Shadow := GTK_SHADOW_ETCHED_IN //3state style
else if (DFCS_CHECKED and uState)<>0 then
Shadow := GTK_SHADOW_IN //checked style
else
Shadow := GTK_SHADOW_OUT; //unchecked style
if (DFCS_PUSHED and uState)<>0 then
State := GTK_STATE_ACTIVE //button pressed(GTK ignores disabled)
else if (DFCS_INACTIVE and uState)<>0 then
State := GTK_STATE_INSENSITIVE //button disabled
else if (DFCS_HOT and uState)<>0 then
State := GTK_STATE_PRELIGHT // button enabled, special (e.g. mouse over)
else
State := GTK_STATE_NORMAL; // button enabled, normal
aDC:=TGtkDeviceContext(DC);
DCOrigin := aDC.Offset;
Style := GetStyle(LazGtkStyleMap[IsRadioButton]);
If Style = nil then begin
Style := GetStyle(lgsGTK_Default);
If Style <> nil then
Style := gtk_style_attach(gtk_style_ref(Style),aDC.Drawable);
end;
Widget := GetStyleWidget(LazGtkStyleMap[IsRadioButton]);
If Widget = nil then
Widget := GetStyleWidget(lgsDefault);
If Widget <> nil then
Widget^.Window := aDC.Drawable;
Result := Style <> nil;
If Result then begin
if IsRadioButton then
gtk_paint_option(Style,aDC.Drawable, State,
Shadow, nil, Widget, 'radiobutton',
R.Left+DCOrigin.X,R.Top+DCOrigin.Y,
R.Right-R.Left, R.Bottom-R.Top)
else
gtk_paint_check(Style,aDC.Drawable, State,
Shadow, nil, Widget, 'checkbutton',
R.Left+DCOrigin.X,R.Top+DCOrigin.Y,
R.Right-R.Left, R.Bottom-R.Top);
end;
end;
var
ClientWidget: PGtkWidget;
begin
Result := False;
if IsValidDC(DC) then
begin
if DevCtx.HasTransf then
begin
R := DevCtx.TransfRectIndirect(Rect);
DevCtx.TransfNormalize(R.Left, R.Right);
DevCtx.TransfNormalize(R.Top, R.Bottom);
end else
R := Rect;
Widget:=TGtkDeviceContext(DC).Widget;
//It's possible to draw in a DC without a widget, e.g., a Bitmap
if Widget <> nil then
begin
ClientWidget:=GetFixedWidget(Widget);
if ClientWidget<>nil then
Widget:=ClientWidget;
end;
end else
Widget:=nil;
case uType of
DFC_CAPTION:
begin //all draw CAPTION commands here
end;
DFC_MENU:
begin
end;
DFC_SCROLL:
begin
end;
DFC_BUTTON:
begin
//DebugLn(Format('Trace: [TGtkWidgetSet.DrawFrameControl] DFC_BUTTON --> draw rect = %d,%d,%d,%d',[R.Left,R.Top,R.Right,R.Bottom]));
//figure out the style first
if (uState and $1F) in [DFCS_BUTTONCHECK, DFCS_BUTTON3STATE] then
begin
//DebugLn('Trace:State ButtonCheck');
DrawCheckOrRadioButton(False);
end
else if (DFCS_BUTTONRADIO and uState) <> 0 then
begin
//DebugLn('Trace:State ButtonRadio');
DrawCheckOrRadioButton(True);
end
else if (DFCS_BUTTONPUSH and uState) <> 0 then
begin
//DebugLn('Trace:State ButtonPush');
DrawButtonPush;
end
else if (DFCS_BUTTONRADIOIMAGE and uState) <> 0 then
begin
//DebugLn('Trace:State ButtonRadioImage');
end
else if (DFCS_BUTTONRADIOMASK and uState) <> 0 then
begin
//DebugLn('Trace:State ButtonRadioMask');
end
else
DebugLn(Format('ERROR: [TGtkWidgetSet.DrawFrameControl] Unknown State 0x%x', [uState]));
end;
else
DebugLn(Format('ERROR: [TGtkWidgetSet.DrawFrameControl] Unknown type %d', [uType]));
end;
end;
function TGTKWidgetSet.DrawFocusRect(DC: HDC; const Rect: TRect): boolean;
var
DevCtx: TGtkDeviceContext absolute DC;
Origin: TPoint;
procedure DrawPixel(X1,Y1: Integer);
begin
inc(X1,Origin.X);
inc(Y1,Origin.Y);
gdk_draw_point(TGtkDeviceContext(DC).Drawable, TGtkDeviceContext(DC).GC, X1, Y1);
end;
procedure DrawVertLine(X1,Y1,Y2: integer);
begin
if Y2<Y1 then
while Y2<Y1 do begin
DrawPixel(X1, Y1);
dec(Y1, 2);
end
else
while Y1<Y2 do begin
DrawPixel(X1, Y1);
inc(Y1, 2);
end;
end;
procedure DrawHorzLine(X1,Y1,X2: integer);
begin
if X2<X1 then
while X2<X1 do begin
DrawPixel(X1, Y1);
dec(X1, 2);
end
else
while X1<X2 do begin
DrawPixel(X1, Y1);
inc(X1, 2);
end;
end;
var
OldROP: Integer;
APen, TempPen: HPEN;
LogPen : TLogPen;
R: TRect;
begin
Result := False;
if IsValidDC(DC) then begin
with LogPen do begin
lopnStyle := PS_DOT;
lopnWidth.X := 2;
lopnColor := clWhite;
end;
if DevCtx.HasTransf then
R := DevCtx.TransfRectIndirect(Rect)
else
R := Rect;
APen := CreatePenIndirect(LogPen);
TempPen := SelectObject(DC, APen);
OldRop := SetROP2(DC, R2_XORPEN);
Origin := DevCtx.Offset;
try
with R do begin
DrawHorzLine(Left, Top, Right-1);
DrawVertLine(Right-1, Top, Bottom-1);
DrawHorzLine(Right-1, Bottom-1, Left);
DrawVertLine(Left, Bottom-1, Top);
end;
Result := True;
finally
SelectObject(DC, TempPen);
DeleteObject(APen);
SetROP2(DC, OldROP);
end;
end;
end;
{------------------------------------------------------------------------------
Function: DrawEdge
Params: DC: HDC; var Rect: TRect; edge: Cardinal; grfFlags: Cardinal
Returns: Boolean
Draws one or more edges of a rectangle. The rectangle is the area
Left to Right-1 and Top to Bottom-1.
------------------------------------------------------------------------------}
function TGtkWidgetSet.DrawEdge(DC: HDC; var ARect: TRect; Edge: Cardinal;
grfFlags: Cardinal): Boolean;
var
DevCtx: TGtkDeviceContext absolute DC;
procedure DrawEdges(var R: TRect; GC: pgdkGC; Drawable:PGdkDrawable;
const TopLeftColor, BottomRightColor: TGDKColor);
begin
gdk_gc_set_foreground(GC, @TopLeftColor);
if (grfFlags and BF_TOP) = BF_TOP then begin
gdk_draw_line(Drawable, GC, R.Left, R.Top, R.Right, R.Top);
inc(R.Top);
end;
if (grfFlags and BF_LEFT) = BF_LEFT then begin
gdk_draw_line(Drawable, GC, R.Left, R.Top, R.Left, R.Bottom);
inc(R.Left);
end;
gdk_gc_set_foreground(GC, @BottomRightColor);
if (grfFlags and BF_BOTTOM) = BF_BOTTOM then begin
gdk_draw_line(Drawable, GC, R.Left, R.Bottom-1, R.Right, R.Bottom-1);
dec(R.Bottom);
end;
if (grfFlags and BF_RIGHT) = BF_RIGHT then begin
gdk_draw_line(Drawable, GC, R.Right-1, R.Top, R.Right-1, R.Bottom);
dec(R.Right);
end;
end;
var
InnerTL, OuterTL,
InnerBR, OuterBR, MiddleColor: TGDKColor;
BInner, BOuter: Boolean;
R: TRect;
DCOrigin: TPoint;
begin
//DebugLn('TGtkWidgetSet.DrawEdge Edge=',DbgS(Edge),8),' grfFlags=',DbgS(Cardinal(grfFlags));
Result := IsValidDC(DC);
if Result
then with TGtkDeviceContext(DC) do
begin
R := ARect;
if DevCtx.HasTransf then
begin
R := DevCtx.TransfRectIndirect(R);
TransfNormalize(R.Left, R.Right);
TransfNormalize(R.Top, R.Bottom);
end;
DCOrigin := Offset;
OffsetRect(R,DCOrigin.X,DCOrigin.Y);
// try to use the gdk functions, so that the current theme is used
BInner := False;
BOuter := False;
// TODO: change this to real colors
if (edge and BDR_RAISEDINNER) = BDR_RAISEDINNER
then begin
InnerTL := AllocGDKColor(GetSysColor(COLOR_BTNHIGHLIGHT));
InnerBR := AllocGDKColor(GetSysColor(COLOR_BTNSHADOW));
BInner := True;
end;
if (edge and BDR_SUNKENINNER) = BDR_SUNKENINNER
then begin
InnerTL := AllocGDKColor(GetSysColor(COLOR_BTNSHADOW));
InnerBR := AllocGDKColor(GetSysColor(COLOR_BTNHIGHLIGHT));
BInner := True;
end;
if (edge and BDR_RAISEDOUTER) = BDR_RAISEDOUTER
then begin
OuterTL := AllocGDKColor(GetSysColor(COLOR_BTNHIGHLIGHT));
OuterBR := AllocGDKColor(GetSysColor(COLOR_BTNSHADOW));
BOuter := True;
end;
if (edge and BDR_SUNKENOUTER) = BDR_SUNKENOUTER
then begin
OuterTL := AllocGDKColor(GetSysColor(COLOR_BTNSHADOW));
OuterBR := AllocGDKColor(GetSysColor(COLOR_BTNHIGHLIGHT));
BOuter := True;
end;
gdk_gc_set_fill(GC, GDK_SOLID);
SelectedColors := dcscCustom;
// Draw outer rect
if BOuter then
DrawEdges(R, GC,Drawable,OuterTL,OuterBR);
// Draw inner rect
if BInner then
DrawEdges(R,GC,Drawable,InnerTL,InnerBR);
// gdk_colormap_free_colors(gdk_colormap_get_system, @OuterTL, 1);
// gdk_colormap_free_colors(gdk_colormap_get_system, @OuterBR, 1);
// gdk_colormap_free_colors(gdk_colormap_get_system, @InnerTL, 1);
// gdk_colormap_free_colors(gdk_colormap_get_system, @InnerBR, 1);
//Draw interiour
if ((grfFlags and BF_MIDDLE) = BF_MIDDLE) then
begin
MiddleColor := AllocGDKColor(GetSysColor(COLOR_BTNFACE));
gdk_gc_set_foreground(GC, @MiddleColor);
gdk_draw_rectangle(Drawable, GC, 1, R.Left, R.Top,
R.Right - R.Left, R.Bottom - R.Top);
end;
// adjust rect if needed
if (grfFlags and BF_ADJUST) = BF_ADJUST then
begin
OffsetRect(R, -DCOrigin.X, -DCOrigin.Y);
ARect := R;
end;
Result := True;
end;
end;
{------------------------------------------------------------------------------
Method: DrawText
Params: DC, Str, Count, Rect, Flags
Returns: If the string was drawn, or CalcRect run
------------------------------------------------------------------------------}
function TGtkWidgetSet.DrawText(DC: HDC; Str: PChar; Count: Integer;
var Rect: TRect; Flags: Cardinal): Integer;
const
TabString = ' ';
var
DevCtx: TGtkDeviceContext absolute DC;
pIndex: Longint;
AStr: String;
TM: TTextmetric;
theRect: TRect;
Lines: PPChar;
I, NumLines: Longint;
TempDC: HDC;
TempPen: HPEN;
TempBrush: HBRUSH;
l: LongInt;
function LeftOffset: Longint;
begin
if (Flags and DT_RIGHT) = DT_RIGHT then
Result := DT_RIGHT
else
if (Flags and DT_CENTER) = DT_CENTER then
Result := DT_CENTER
else
Result := DT_LEFT;
end;
function TopOffset: Longint;
begin
if (Flags and DT_BOTTOM) = DT_BOTTOM then
Result := DT_BOTTOM
else
if (Flags and DT_VCENTER) = DT_VCENTER then
Result := DT_VCENTER
else
Result := DT_TOP;
end;
function CalcRect: Boolean;
begin
Result := (Flags and DT_CALCRECT) = DT_CALCRECT;
end;
function TextExtentPoint(Str: PChar; Count: Integer; var Sz: TSize): Boolean;
var
NewStr: String;
begin
if (Flags and DT_EXPANDTABS) <> 0 then
begin
NewStr := StringReplace(Str, #9, TabString, [rfReplaceAll]);
Result := GetTextExtentPoint(DC, PChar(NewStr), Length(NewStr), Sz);
end
else
Result := GetTextExtentPoint(Dc, Str, Count, Sz);
end;
procedure DoCalcRect;
var
AP: TSize;
J, MaxWidth,
LineWidth: Integer;
begin
theRect := Rect;
MaxWidth := theRect.Right - theRect.Left;
if (Flags and DT_SINGLELINE) > 0 then
begin
// ignore word and line breaks
TextExtentPoint(PChar(AStr), length(AStr), AP);
theRect.Bottom := theRect.Top + TM.tmHeight;
if (Flags and DT_CALCRECT)<>0 then
theRect.Right := theRect.Left + AP.cX
else
begin
theRect.Right := theRect.Left + Min(MaxWidth, AP.cX);
if (Flags and DT_VCENTER) > 0 then
begin
OffsetRect(theRect, 0, ((Rect.Bottom - Rect.Top) - (theRect.Bottom - theRect.Top)) div 2);
{$ifdef Gtk1}
//gtk1 overestimate TM.tmHeight leading to wrong calculation of the center offset
OffsetRect(theRect, 0, 1);
{$endif}
end
else
if (Flags and DT_BOTTOM) > 0 then
begin
OffsetRect(theRect, 0, (Rect.Bottom - Rect.Top) - (theRect.Bottom - theRect.Top));
end;
end;
end
else
begin
// consider line breaks
if (Flags and DT_WORDBREAK) = 0 then
begin
// do not break at word boundaries
TextExtentPoint(PChar(AStr), length(AStr), AP);
MaxWidth := AP.cX;
end;
Self.WordWrap(DC, PChar(AStr), MaxWidth, Lines, NumLines);
if (Flags and DT_CALCRECT)<>0 then
begin
LineWidth := 0;
if (Lines <> nil) then
begin
for J := 0 to NumLines - 1 do
begin
TextExtentPoint(Lines[J], StrLen(Lines[J]), AP);
LineWidth := Max(LineWidth, AP.cX);
end;
end;
LineWidth := Min(MaxWidth, LineWidth);
end else
LineWidth := MaxWidth;
theRect.Right := theRect.Left + LineWidth;
theRect.Bottom := theRect.Top + NumLines*TM.tmHeight;
if NumLines>1 then
Inc(theRect.Bottom, (NumLines-1)*TM.tmDescent);// space between lines
//debugln('TGtkWidgetSet.DrawText A ',dbgs(theRect),' TM.tmHeight=',dbgs(TM.tmHeight),' LineWidth=',dbgs(LineWidth),' NumLines=',dbgs(NumLines));
end;
if not CalcRect then
case LeftOffset of
DT_CENTER:
OffsetRect(theRect, (Rect.Right - theRect.Right) div 2, 0);
DT_RIGHT:
OffsetRect(theRect, Rect.Right - theRect.Right, 0);
end;
end;
procedure DrawLineRaw(theLine: PChar; LineLength, TopPos: Longint);
var
Points: array[0..1] of TSize;
LeftPos: Longint;
begin
if LeftOffset <> DT_LEFT then
GetTextExtentPoint(DC, theLine, LineLength, Points[0]);
if TempBrush = HBRUSH(-1) then
TempBrush := SelectObject(DC, GetStockObject(NULL_BRUSH));
case LeftOffset of
DT_LEFT:
LeftPos := theRect.Left;
DT_CENTER:
LeftPos := theRect.Left + (theRect.Right - theRect.Left) div 2
- Points[0].cX div 2;
DT_RIGHT:
LeftPos := theRect.Right - Points[0].cX;
end;
// Draw line of Text
TextUtf8Out(DC, LeftPos, TopPos, theLine, lineLength);
end;
procedure DrawLine(theLine: PChar; LineLength, TopPos: Longint);
var
Points: array[0..1] of TSize;
LogP: TLogPen;
LeftPos: Longint;
begin
if TempBrush = HBRUSH(-1) then
TempBrush := SelectObject(DC, GetStockObject(NULL_BRUSH));
if LeftOffset <> DT_Left then
GetTextExtentPoint(DC, theLine, LineLength, Points[0]);
case LeftOffset of
DT_LEFT:
LeftPos := theRect.Left;
DT_CENTER:
LeftPos := theRect.Left + (theRect.Right - theRect.Left) div 2
- Points[0].cX div 2;
DT_RIGHT:
LeftPos := theRect.Right - Points[0].cX;
end;
// Draw line of Text
TextUtf8Out(DC, LeftPos, TopPos, theLine, LineLength);
// Draw Prefix
if (pIndex > 0) and (pIndex<=LineLength) then
begin
// Create & select pen of font color
if TempPen = HPEN(-1) then
begin
LogP.lopnStyle := PS_SOLID;
LogP.lopnWidth.X := 1;
LogP.lopnColor := GetTextColor(DC);
TempPen := SelectObject(DC, CreatePenIndirect(LogP));
end;
{Get prefix line position}
GetTextExtentPoint(DC, theLine, pIndex - 1, Points[0]);
Points[0].cX := LeftPos + Points[0].cX;
Points[0].cY := TopPos + tm.tmHeight - TM.tmDescent + 1;
GetTextExtentPoint(DC, @aStr[pIndex], 1, Points[1]);
Points[1].cX := Points[0].cX + Points[1].cX;
Points[1].cY := Points[0].cY;
{Draw prefix line}
Polyline(DC, PPoint(@Points[0]), 2);
end;
end;
begin
if (Str=nil) or (Str[0]=#0) then Exit(0);
//DebugLn(Format('trace:> [TGtkWidgetSet.DrawText] DC:0x%x, Str:''%s'', Count: %d, Rect = %d,%d,%d,%d, Flags:%d',
// [DC, Str, Count, Rect.Left, Rect.Top, Rect.Right, Rect.Bottom, Flags]));
if not IsValidDC(DC) then Exit(0);
if (Count < -1) or (IsRectEmpty(Rect) and ((Flags and DT_CALCRECT) = 0)) then Exit(0);
// Don't try to use StrLen(Str) in cases count >= 0
// In those cases str is NOT required to have a null terminator !
if Count = -1 then Count := StrLen(Str);
Lines := nil;
NumLines := 0;
TempDC := HDC(-1);
TempPen := HPEN(-1);
TempBrush := HBRUSH(-1);
try
if (Flags and (DT_SINGLELINE or DT_CALCRECT or DT_NOPREFIX or DT_NOCLIP or DT_EXPANDTABS)) =
(DT_SINGLELINE or DT_NOPREFIX or DT_NOCLIP)
then begin
//DebugLn(['TGtkWidgetSet.DrawText Calc single line']);
CopyRect(theRect, Rect);
DrawLineRaw(Str, Count, Rect.Top);
Result := Rect.Bottom - Rect.Top;
Exit;
end;
SetLength(AStr,Count);
if Count>0 then
System.Move(Str^,AStr[1],Count);
if (Flags and DT_EXPANDTABS) <> 0 then
AStr := StringReplace(AStr, #9, TabString, [rfReplaceAll]);
if (Flags and DT_NOPREFIX) <> DT_NOPREFIX then
begin
pIndex := DeleteAmpersands(AStr);
if pIndex > Length(AStr) then
pIndex := -1; // String ended in '&', which was deleted
end
else
pIndex := -1;
GetTextMetrics(DC, TM);
DoCalcRect;
Result := theRect.Bottom - theRect.Top;
if (Flags and DT_CALCRECT) = DT_CALCRECT
then begin
//DebugLn(['TGtkWidgetSet.DrawText Complex Calc']);
CopyRect(Rect, theRect);
exit;
end;
TempDC := SaveDC(DC);
if (Flags and DT_NOCLIP) <> DT_NOCLIP
then begin
if theRect.Right > Rect.Right then
theRect.Right := Rect.Right;
if theRect.Bottom > Rect.Bottom then
theRect.Bottom := Rect.Bottom;
IntersectClipRect(DC, theRect.Left, theRect.Top,
theRect.Right, theRect.Bottom);
end;
if (Flags and DT_SINGLELINE) = DT_SINGLELINE
then begin
//DebugLn(['TGtkWidgetSet.DrawText Draw single line']);
DrawLine(PChar(AStr), length(AStr), theRect.Top);
Exit; //we're ready
end;
// multiple lines
if Lines = nil then Exit; // nothing to do
if NumLines = 0 then Exit; //
//DebugLn(['TGtkWidgetSet.DrawText Draw multiline']);
for i := 0 to NumLines - 1 do
begin
if theRect.Top > theRect.Bottom then Break;
if ((Flags and DT_EDITCONTROL) = DT_EDITCONTROL)
and (tm.tmHeight > (theRect.Bottom - theRect.Top))
then Break;
if Lines[i] <> nil then begin
l:=StrLen(Lines[i]);
DrawLine(Lines[i], l, theRect.Top);
dec(pIndex,l+length(LineEnding));
end;
Inc(theRect.Top, TM.tmDescent + TM.tmHeight);// space between lines
end;
finally
Reallocmem(Lines, 0);
if TempBrush <> HBRUSH(-1) then
SelectObject(DC, TempBrush);// DeleteObject not needed here, because it was a default Brush
if TempPen <> HPEN(-1) then
DeleteObject(SelectObject(DC, TempPen));
if TempDC <> HDC(-1) then
RestoreDC(DC, TempDC);
end;
end;
{------------------------------------------------------------------------------
Function: EnableScrollBar
Params: Wnd, wSBflags, wArrows
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.EnableScrollBar(Wnd: HWND; wSBflags, wArrows: Cardinal): Boolean;
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.EnableScrollBar]');
//TODO: Implement this;
Result := False;
end;
{------------------------------------------------------------------------------
Function: EnableWindow
Params: hWnd:
bEnable:
Returns:
------------------------------------------------------------------------------}
function TGtkWidgetSet.EnableWindow(hWnd: HWND; bEnable: Boolean): Boolean;
begin
//DebugLn(Format('Trace: [TGtkWidgetSet.EnableWindow] hWnd: 0x%x, Enable: %s', [hwnd, BOOL_TEXT[bEnable]]));
if hWnd <> 0 then
gtk_widget_set_sensitive(pgtkwidget(hWnd), bEnable);
Result:=true;
end;
{------------------------------------------------------------------------------
Function: EndPaint
Params:
Returns:
------------------------------------------------------------------------------}
function TGtkWidgetSet.EndPaint(Handle: hwnd; var PS: TPaintStruct): Integer;
var
Widget: PGtkWidget;
Info: PWidgetInfo;
{$IFDEF Gtk1}
DevCtx: TGtkDeviceContext;
DCDrawable: PGdkDrawable;
Width, Height: integer;
CaretWasVisible: Boolean;
MainWidget: PGtkWidget;
{$ENDIF}
begin
Result:=1;
if PS.HDC = 0 then Exit;
Widget := PGtkWidget(Handle);
Info:=GetWidgetInfo(Widget,false);
if Info<>nil then
dec(Info^.PaintDepth);
{$IFDEF Gtk1}
DevCtx := TGtkDeviceContext(PS.HDC);
if Widget <> DevCtx.Widget then
RaiseGDBException('Gtk paint event for other than our window');
DCDrawable := DevCtx.Drawable;
if dcfDoubleBuffer in DevCtx.Flags
then begin
// copy
gdk_window_get_size(DCDrawable, @Width, @Height);
{$IFDEF VerboseDoubleBuffer}
DebugLn('TGtkWidgetSet.EndPaint Copying from buffer to window: ',Width,' ',Height);
{$ENDIF}
gdk_gc_set_clip_region(DevCtx.GC, nil);
gdk_gc_set_clip_rectangle(DevCtx.GC, nil);
// hide caret
// mwe: note that this call is just a bunch of code to see if widget is our winapiwidget
HideCaretOfWidgetGroup(Widget, MainWidget, CaretWasVisible);
// draw
gdk_window_copy_area(Widget^.Window, DevCtx.GC, 0,0, DCDrawable, 0, 0, Width, Height);
// restore caret
if CaretWasVisible then
GTKAPIWidget_ShowCaret(PGTKAPIWidget(MainWidget));
end;
{$ENDIF}
ReleaseDC(Handle, PS.HDC);
end;
function TGTKWidgetSet.EnumDisplayMonitors(hdc: HDC; lprcClip: PRect;
lpfnEnum: MonitorEnumProc; dwData: LPARAM): LongBool;
begin
Result := lpfnEnum(1, 0, nil, dwData);
end;
{.$define VerboseEnumFonts}
{$IFDEF VerboseGtkToDos}{$note: compare TGtkWidgetSet.EnumFontFamilies with gtkproc.FillScreenFonts}{$ENDIF}
function TGtkWidgetSet.EnumFontFamilies(DC: HDC; Family: Pchar;
EnumFontFamProc: FontEnumProc; LParam:Lparam):longint;
var
DevCtx: TGtkDeviceContext absolute DC;
xFonts: PPChar;
FontList: TStringList;
EnumLogFont: TEnumLogFont;
Metric: TNewTextMetric;
I,N: Integer;
tmp: String;
FontType: Integer;
begin
result := 0;
if not Assigned(EnumFontFamProc) then begin
result := 2;
DebugLn('EnumFontFamProc Callback not set');
// todo: raise exception?
exit;
end;
FontList := TStringlist.Create;
try
if Family<>'' then
Tmp := '-*-'+Family+'-*-*-*-*-*-*-*-*-*-*-*-*'
else
Tmp := '-*'; // get rid of aliases
{$ifdef VerboseEnumFonts}
WriteLn('Looking for fonts matching: ', tmp);
{$endif}
{$ifdef HasX}
XFonts := XListFonts(gdk_display, pchar(Tmp), 10000, @N);
{$else}
{$IFDEF VerboseGtkToDos}{$warning implement getting XFonts for this OS}{$ENDIF}
XFonts := nil;
N:=0;
{$endif}
try
for I := 0 to N - 1 do
if XFonts[I] <> nil then begin
Tmp := ExtractFamilyFromXLFDName(XFonts[I]);
{$ifdef VerboseEnumFonts}
WriteLn(I:5,' [', tmp, '] Font=',XFonts[i]);
{$endif}
if Tmp <> '' then begin
if family='' then begin
// get just the font names
if FontList.IndexOf(Tmp) < 0 then begin
EnumLogFont.elfLogFont := XLFDNameToLogFont(XFonts[i]);
FillChar(Metric, SizeOf(Metric), #0);
FontType := 0; // todo: GetFontTypeFromXLDF or FontId
EnumLogFont.elfFullName := '';
EnumFontFamProc(EnumLogFont, Metric, FontType, Lparam);
FontList.Append(Tmp);
end;
end else begin
EnumLogFont.elfLogFont := XLFDNameToLogFont(XFonts[i]);
EnumlogFont.elfFullname := '';
EnumLogFont.elfStyle := '';
FillChar(Metric, SizeOf(Metric), #0);
FontType := 0; // todo: GetFontTypeFromXLDF or FontId
EnumFontFamProc(EnumLogFont, Metric, FontType, Lparam);
end;
end;
end;
finally
{$ifdef HasX}
XFreeFontNames(XFonts);
{$endif}
end;
finally
Fontlist.Free;
end;
end;
function TGtkWidgetSet.EnumFontFamiliesEx(DC: HDC; lpLogFont: PLogFont;
Callback: FontEnumExProc; Lparam:LParam; Flags: dword): longint;
var
DevCtx: TGtkDeviceContext absolute DC;
type
TXLFD=record
Foundry: string[15];
Family, CharsetReg, CharsetCod: string[32];
WeightName,widthName,StyleName: string[20];
Slant: string[5];
PixelSize,PointSize,ResX,ResY: Integer;
end;
var
Xlfd: TXLFD;
CharsetFilter: TStringList;
PitchFilter: TStringList;
EnumLogFont: TEnumLogFontEx;
Metric: TNewTextMetricEx;
function ParseXLFDFont(const font: string): boolean;
function MyStrToIntDef(const s: string; def: integer): integer;
begin
result := StrToIntDef(s, Def);
if result=0 then
result := def
end;
begin
result := IsFontNameXLogicalFontDesc(font);
fillchar(Xlfd, SizeOf(Xlfd), 0);
if result then with Xlfd do begin
Foundry := ExtractXLFDItem(Font, XLFD_FOUNDRY);
Family := ExtractXLFDItem(Font, XLFD_FAMILY);
CharsetReg := ExtractXLFDItem(Font, XLFD_CHARSET_REG);
CharSetCod := ExtractXLFDItem(Font, XLFD_CHARSET_COD);
WeightName := ExtractXLFDItem(Font, XLFD_WEIGHTNAME);
Slant := ExtractXLFDItem(Font, XLFD_SLANT);
WidthName := ExtractXLFDItem(Font, XLFD_WIDTHNAME);
StyleName := ExtractXLFDItem(Font, XLFD_STYLENAME);
ResX := MyStrToIntDef(ExtractXLFDItem(Font, XLFD_RESX), 72);
ResY := MyStrToIntDef(ExtractXLFDItem(Font, XLFD_RESX), 72);
PixelSize := StrToIntDef(ExtractXLFDItem(Font, XLFD_PIXELSIZE), 0);
PointSize := StrToIntDef(ExtractXLFDItem(Font, XLFD_POINTSIZE), 0);
end;
end;
function XLFDToFontStyle: string;
var
s: string;
begin
result := xlfd.WeightName;
s :=lowercase(xlfd.Slant);
if s='i' then result := result + ' '+ 'italic' else
if s='o' then result := result + ' '+ 'oblique' else
if s='ri' then result := result + ' '+ 'reverse italic' else
if s='ro' then result := result + ' '+ 'reverse oblique'
else begin
if (S<>'r')and(S<>'') then
result := result + ' ' + S;
end;
end;
procedure QueueCharsetFilter(Charset: byte);
var
i: integer;
rec: PCharsetEncodingRec;
s: string;
begin
for i:=0 to CharsetEncodingList.count-1 do begin
Rec := CharsetEncodingList[i];
if (Rec=nil) or (Rec^.CharSet<>Charset) or (not Rec^.EnumMap) then
continue;
s := Rec^.CharSetReg;
if Rec^.CharsetRegPart then
s := s + '*';
s := s + '-' + Rec^.CharSetCod;
if Rec^.CharsetCodPart then
s := s + '*';
CharsetFilter.Add(s);
end;
end;
procedure QueuePitchFilter(Pitch: byte);
begin
if pitch and FIXED_PITCH = FIXED_PITCH then begin
PitchFilter.Add('m');
PitchFilter.Add('c'); // character cell it's also fixed pitch
end;
if pitch and VARIABLE_PITCH = VARIABLE_PITCH then
PitchFilter.Add('p');
if pitch and MONO_FONT = MONO_FONT then
PitchFilter.Add('m');
if PitchFilter.Count=0 then
PitchFilter.Add('*');
end;
function XLFDToCharset: byte;
const
CharsetPriority: array[1..19] of byte =
(
SYMBOL_CHARSET, MAC_CHARSET, SHIFTJIS_CHARSET,
HANGEUL_CHARSET, JOHAB_CHARSET, GB2312_CHARSET,
CHINESEBIG5_CHARSET, GREEK_CHARSET, TURKISH_CHARSET,
VIETNAMESE_CHARSET, HEBREW_CHARSET, ARABIC_CHARSET,
BALTIC_CHARSET, RUSSIAN_CHARSET, THAI_CHARSET,
EASTEUROPE_CHARSET, OEM_CHARSET, FCS_ISO_10646_1,
ANSI_CHARSET
);
var
i,n: integer;
rec: PCharsetEncodingRec;
begin
for i := Low(CharsetPriority) to High(CharsetPriority) do
for n:= 0 to CharsetEncodingList.count-1 do begin
rec := CharsetEncodingList[n];
if (rec=nil) or (rec^.CharSet<>CharsetPriority[i]) then
continue;
// try to match registry part
if rec^.CharSetReg<>'*' then begin
if rec^.CharsetRegPart then begin
if pos(rec^.CharSetReg, xlfd.CharsetReg)=0 then
continue;
end else begin
if AnsiCompareText(Rec^.CharSetReg, xlfd.CharsetReg) <> 0 then
continue;
end;
end;
// try to match coding part
if rec^.CharSetCod<>'*' then begin
if rec^.CharsetCodPart then begin
if pos(rec^.CharSetCod, xlfd.CharsetCod)=0 then
continue;
end else begin
if AnsiCompareText(Rec^.CharSetCod, xlfd.CharsetCod) <> 0 then
continue;
end;
end;
// this one is good enought to match bot registry and encondig part
result := CharsetPriority[i];
exit;
end;
result := DEFAULT_CHARSET;
end;
function XLFDCharsetToScript: string;
begin
result := xlfd.CharsetReg + '-' + xlfd.CharsetCod;
end;
function FoundryAndFamilyFilter(const FaceName: string): string;
var
foundry,family: string;
i: LongInt;
begin
if FaceName='' then begin
family := '*';
foundry := '*';
end else begin
family := FaceName;
// look for foundry encoded in family name
i := pos(FOUNDRYCHAR_OPEN, family);
if i<>0 then begin
Foundry := copy(Family, i+1, Length(Family));
family := trim(copy(family, 1, i-1));
i := pos(FOUNDRYCHAR_CLOSE, Foundry);
if i<>0 then
Delete(Foundry, i, Length(Foundry))
else
; // ill formed but it's ok.
end else
Foundry := '*';
end;
result := Foundry+'-'+Family;
end;
function XLFDFamilyFace: string;
begin
with xlfd do
if (Length(Foundry)>0) and (Length(Family) + length(Foundry) + 3 < 31) then
result := Family + ' '+ FOUNDRYCHAR_OPEN + Foundry + FOUNDRYCHAR_CLOSE
else
result := Family;
end;
function XLFDToFontType: integer;
begin
if ((xlfd.PointSize=0) and (xlfd.PixelSize=0))
or ((xlfd.PointSize=120) and (xlfd.PixelSize=17)) // see bug 16298
then
result := TRUETYPE_FONTTYPE
else
result := RASTER_FONTTYPE or DEVICE_FONTTYPE;
end;
// process the current xlfd font, if user returns 0 from callback finish
function ProcessXFont(const index: integer; const font: string;
FontList: TStringList): boolean;
var
FontType: Integer;
tmp: string;
FullSearch: boolean;
begin
FullSearch := ( lpLogFont^.lfFaceName = '');
result := false;
with xlfd, EnumLogFont do
if FullSearch then begin
//
// quick enumeration of fonts, make sure this is
// documented because only some fields are filled !!!
//
Foundry := ExtractXLFDItem(Font, XLFD_FOUNDRY);
Family := ExtractXLFDItem(Font, XLFD_FAMILY);
tmp := XLFDFamilyFace();
if FontList.IndexOf(tmp) < 0 then begin
PixelSize := StrToIntDef(ExtractXLFDItem(Font, XLFD_PIXELSIZE), 0);
PointSize := StrToIntDef(ExtractXLFDItem(Font, XLFD_POINTSIZE), 0);
CharsetReg := ExtractXLFDItem(Font, XLFD_CHARSET_REG);
CharsetCod := ExtractXLFDItem(Font, XLFD_CHARSET_COD);
FontType := XLFDToFontType();
elfLogFont.lfCharSet := XLFDToCharset();
elfLogFont.lfFaceName := tmp;
result := Callback(EnumLogFont, Metric, FontType, LParam)=0;
FontList.Append(tmp);
end;
end else
if ParseXLFDFont(Font) then begin
//
// slow enumeration of fonts, only if face is present
//
// family
tmp := XLFDFamilyFace();
{$ifdef verboseEnumFonts}
DebugLn(dbgs(index),' face=', tmp, ' Font=', Font);
{$endif}
//if FontList.IndexOf(tmp) < 0 then begin
// Fonttype
FontType := XLFDToFontType();
// LogFont
elfLogFont := XLFDNameToLogFont(Font);
elfLogFont.lfFaceName := tmp;
elfLogFont.lfCharSet := XLFDToCharset();
// from logfont
elfStyle := XLFDToFontStyle();
elfScript := XLFDCharsetToScript();
// tempted to feed here full xlfd, but 63 chars might be to small
if Foundry = '' then
elfFullName := Family
else
elfFullName := Foundry + ' ' + Family ;
// Metric
//
fillchar(metric.ntmeFontSignature,
sizeOf(metric.ntmeFontSignature), 0);
with metric.ntmentm do begin
tmheight := elfLogFont.lfHeight;
tmAveCharWidth := elfLogFont.lfWidth;
tmWeight := elfLogFont.lfWeight;
tmDigitizedAspectX := ResX;
tmDigitizedAspectY := ResY;
tmItalic := elfLogFont.lfItalic;
tmUnderlined := elfLogFont.lfUnderline;
tmStruckOut := elfLogFont.lfStrikeOut;
tmPitchAndFamily := elfLogFont.lfPitchAndFamily;
tmCharSet := elfLogFont.lfCharSet;
// todo fields
tmMaxCharWidth := elfLogFont.lfWidth; // todo
tmAscent := 0; // todo
tmDescent := 0; // todo
tmInternalLeading := 0; // todo
tmExternalLeading := 0; // todo
tmOverhang := 0; // todo;
tmFirstChar := ' '; // todo, atm ascii
tmLastChar := #255; // todo, atm ascii
tmDefaultChar := '.'; // todo, atm dot
tmBreakChar := ' '; // todo, atm space
ntmFlags := 0; // todo combination of NTM_XXXX constants
ntmSizeEM := tmHeight; // todo
ntmCellHeight := ntmSizeEM; // todo
ntmAvgWidth := ntmSizeEM; // todo
end; // with metric.ntmentm do ...
// do callback
result := Callback(EnumLogFont, Metric, FontType, LParam) = 0;
FontList.Append(tmp);
//end; // if not FullSearch or (FontList.IndexOf(tmp) < 0 then ...
end; // with xlfd, EnumLogFont do ...
end;
var
xFonts: PPChar;
FontList: TStringList;
I,J,K,N: Integer;
Tmp,FandF: String;
begin
result := 0;
// initial checks
if not Assigned(Callback) then begin
result := 2;
DebugLn('EnumFontFamiliesEx: EnumFontFamProcEx Callback not set');
// todo: raise exception?
exit;
end;
if not Assigned(lpLogFont) then begin
result := 3;
DebugLn('EnumFontFamiliesEx: lpLogFont not set');
// todo: enumerate all fonts?
exit;
end;
// foundry and family filter
FandF := FoundryAndFamilyFilter(lpLogFont^.lfFaceName);
FontList := TStringlist.Create;
CharSetFilter := TStringList.Create;
PitchFilter := TStringList.Create;
PitchFilter.Duplicates := dupIgnore;
try
QueueCharSetFilter(lpLogFont^.lfCharSet);
QueuePitchFilter(lpLogFont^.lfPitchAndFamily);
{$ifdef verboseEnumFonts}
for j:=0 to CharSetFilter.Count-1 do begin
// pitch filter is guaranteed to have at least one element
for k:=0 to PitchFilter.Count-1 do begin
tmp := '-'+FAndF+'-*-*-*-*-*-*-*-*-'+PitchFilter[k]+'-*-'+CharSetFilter[j];
DebugLn('EnumFontFamiliesEx: will enumerate fonts matching: ', tmp);
end;
end;
{$endif}
for j:=0 to CharSetFilter.Count-1 do begin
for k:=0 to PitchFilter.Count-1 do begin
tmp := '-'+FAndF+'-*-*-*-*-*-*-*-*-'+PitchFilter[k]+'-*-'+CharSetFilter[j];
{$ifdef HasX}
XFonts := XListFonts(gdk_display, pchar(Tmp), 10000, @N);
{$else}
{$IFDEF VerboseGtkToDos}{$warning implement getting XFonts for this OS}{$ENDIF}
XFonts := nil;
N:=0;
{$endif}
try
{$ifdef VerboseEnumFonts}
DebugLn('EnumFontFamiliesEx: found ',dbgs(N),' fonts matching: ', tmp);
{$endif}
for i:=0 to N-1 do
if XFonts[i]<>nil then
if ProcessXFont(i, XFonts[i], FontList) then
break;
finally
{$ifdef HasX}
XFreeFontNames(XFonts);
{$endif}
end;
end;
end;
finally
PitchFilter.Free;
Fontlist.Free;
CharSetFilter.Free;
end;
end;
{------------------------------------------------------------------------------
Method: Ellipse
Params: X1, Y1, X2, Y2
Returns: Nothing
Use Ellipse to draw a filled circle or ellipse.
------------------------------------------------------------------------------}
function TGtkWidgetSet.Ellipse(DC: HDC; X1, Y1, X2, Y2: Integer): Boolean;
var
DevCtx: TGtkDeviceContext absolute DC;
Left, Top, Width, Height: Integer;
DCOrigin: TPoint;
begin
Result := IsValidDC(DC);
if not Result then Exit;
if DevCtx.HasTransf then
DevCtx.TransfRect(X1, Y1, X2, Y2);
CalculateLeftTopWidthHeight(X1, Y1, X2, Y2, Left, Top, Width, Height);
if (Width = 0) or (Height = 0) then Exit(True);
// X2, Y2 is not part of the rectangle
dec(Width);
dec(Height);
// first draw interior in brush color
DCOrigin := DevCtx.Offset;
{$IFDEF DebugGDKTraps}
BeginGDKErrorTrap;
{$ENDIF}
if not DevCtx.IsNullBrush then
begin
DevCtx.SelectBrushProps;
gdk_draw_arc(DevCtx.Drawable, DevCtx.GC, 1,
Left+DCOrigin.X, Top+DCOrigin.Y, Width, Height, 0, 360 shl 6);
end;
// Draw outline
DevCtx.SelectPenProps;
if (dcfPenSelected in DevCtx.Flags) then
begin
Result := True;
if not DevCtx.IsNullPen then
begin
gdk_draw_arc(DevCtx.Drawable, DevCtx.GC, 0,
Left+DCOrigin.X, Top+DCOrigin.Y, Width, Height, 0, 360 shl 6);
end;
end
else
Result := False;
{$IFDEF DebugGDKTraps}
EndGDKErrorTrap;
{$ENDIF}
end;
{------------------------------------------------------------------------------
Method: EqualRgn
Params: Rgn1: HRGN; Rgn2: HRGN
Returns: True if the two regions are equal
Checks the two specified regions to determine whether they are identical. The
function considers two regions identical if they are equal in size and shape.
------------------------------------------------------------------------------}
function TGtkWidgetSet.EqualRgn(Rgn1: HRGN; Rgn2: HRGN): Boolean;
var
AGdiObject: PGdiObject absolute Rgn1;
BGdiObject: PGdiObject absolute Rgn2;
begin
Result := IsValidGDIObject(Rgn1) and IsValidGDIObject(Rgn2);
if Result then
Result := gdk_region_equal(AGdiObject^.GDIRegionObject,
BGdiObject^.GDIRegionObject);
end;
{------------------------------------------------------------------------------
Function: ExcludeClipRect
Params: dc: hdc; Left, Top, Right, Bottom : Integer
Returns: integer
Subtracts all intersecting points of the passed bounding rectangle
(Left, Top, Right, Bottom) from the Current clipping region in the
device context (dc).
The result can be one of the following constants
Error
NullRegion
SimpleRegion
ComplexRegion
------------------------------------------------------------------------------}
function TGtkWidgetSet.ExcludeClipRect(dc: hdc;
Left, Top, Right, Bottom : Integer) : Integer;
begin
Result := Inherited ExcludeClipRect(DC, Left, Top, Right, Bottom);
end;
function TGTKWidgetSet.ExtCreatePen(dwPenStyle, dwWidth: DWord;
const lplb: TLogBrush; dwStyleCount: DWord; lpStyle: PDWord): HPEN;
var
GObject: PGdiObject;
i: integer;
begin
GObject := NewGDIObject(gdiPen);
GObject^.UnTransfPenWidth := 0;
GObject^.IsExtPen := True;
GObject^.GDIPenStyle := dwPenStyle;
GObject^.GDIPenWidth := dwWidth;
SetGDIColorRef(GObject^.GDIPenColor, lplb.lbColor);
GObject^.GDIPenDashesCount := dwStyleCount;
if dwStyleCount > 0 then
begin
GetMem(GObject^.GDIPenDashes, dwStyleCount * SizeOf(gint8));
for i := 0 to dwStyleCount - 1 do
GObject^.GDIPenDashes[i] := lpStyle[i];
end;
Result := HPEN(PtrUInt(GObject));
end;
{------------------------------------------------------------------------------
Function: ExtSelectClipRGN
Params: dc, RGN, Mode
Returns: integer
Combines the passed Region with the current clipping region in the device
context (dc), using the specified mode.
The Combine Mode can be one of the following:
RGN_AND : all points which are in both regions
RGN_COPY : an exact copy of the source region, same as SelectClipRGN
RGN_DIFF : all points which are in the Clipping Region but
not in the Source.(Clip - RGN)
RGN_OR : all points which are in either the Clip Region or
in the Source.(Clip + RGN)
RGN_XOR : all points which are in either the Clip Region
or in the Source, but not in both.
The result can be one of the following constants
Error
NullRegion
SimpleRegion
ComplexRegion
------------------------------------------------------------------------------}
function TGtkWidgetSet.ExtSelectClipRGN(dc: hdc; rgn : hrgn;
Mode : Longint) : Integer;
var
Clip,
Tmp : hRGN;
X, Y : Longint;
DCOrigin: TPoint;
begin
Result := SIMPLEREGION;
If not IsValidDC(DC) then
Result := ERROR
else with TGtkDeviceContext(DC) do
begin
//DebugLn('TGtkWidgetSet.ExtSelectClipRGN A ClipRegValid=',dbgs(DCClipRegionValid(DC)),
// ' Mode=',dbgs(Mode),' RGN=',GDKRegionAsString(PGdiObject(RGN)^.GDIRegionObject));
If ClipRegion=nil then begin
// there is no clipping region in the DC
Case Mode of
RGN_COPY:
begin
Result := RegionType(PGdiObject(RGN)^.GDIRegionObject);
If Result <> ERROR then
Result := SelectClipRGN(DC, RGN);
end;
RGN_OR,
RGN_XOR,
RGN_AND,
RGN_DIFF:
begin
// get existing clip
GDK_Window_Get_Size(Drawable, @X, @Y);
DCOrigin:= Offset;
Clip := CreateRectRGN(-DCOrigin.X,-DCOrigin.Y,X-DCOrigin.X,Y-DCOrigin.Y);
// create target clip
Tmp := CreateEmptyRegion;
// combine
Result := CombineRGN(Tmp, Clip, RGN, Mode);
// commit
//DebugLn('TGtkWidgetSet.ExtSelectClipRGN B ClipRegValid=',dbgs(ClipRegion),' TmpRGN=',GDKRegionAsString(PGdiObject(Tmp)^.GDIRegionObject));
SelectClipRGN(DC, Tmp);
// clean up
DeleteObject(Clip);
DeleteObject(Tmp);
end;
end;
end
else
Result := inherited ExtSelectClipRGN(dc, rgn, mode);
end;
end;
{------------------------------------------------------------------------------
Function: ExtTextOut
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.ExtTextOut(DC: HDC; X, Y: Integer; Options: Longint;
Rect: PRect; Str: PChar; Count: Longint; Dx: PInteger): Boolean;
{$Ifdef GTK2}
begin
DebugLn('ToDo: TGtkWidgetSet.ExtTextOut');
Result:=false;
end;
{$Else}
var
DevCtx: TGtkDeviceContext absolute DC;
LineStart, LineEnd, StrEnd: PChar;
Left, Top, Width, Height: Integer;
TopY, LineLen, LineHeight : Integer;
TxtPt : TPoint;
UseFont : PGDKFont;
DCOrigin: TPoint;
UnderLine: boolean;
buffer: PGdkDrawable;
buffered: Boolean;
procedure DrawTextLine;
var
UnderLineLen, Y: integer;
CurDistX: PInteger;
CharsWritten, CurX, i: integer;
LinePos: PChar;
CharLen: LongInt;
begin
{$IFDEF DebugGDKTraps}BeginGDKErrorTrap;{$ENDIF}
if Dx = nil
then begin
// no dist array -> write as one block
gdk_draw_text(Buffer, UseFont, DevCtx.GC, TxtPt.X, TxtPt.Y, LineStart, LineLen);
end
else begin
// dist array -> write each char separately
CharsWritten := Integer(LineStart-Str);
if DevCtx.DCTextMetric.IsDoubleByteChar
then begin
CharLen := 2;
CharsWritten := CharsWritten div 2;
end
else CharLen := 1;
CurDistX := Dx+CharsWritten*SizeOf(Integer);
CurX := TxtPt.X;
LinePos := LineStart;
i:=1;
while i <= LineLen do
begin
gdk_draw_text(Buffer, UseFont, DevCtx.GC, CurX, TxtPt.Y, LinePos, CharLen);
inc(LinePos,CharLen);
inc(CurX,CurDistX^);
inc(CurDistX);
inc(i,CharLen);
end;
end;
if UnderLine
then begin
if Rect <> nil
then
UnderLineLen := Rect^.Right-Rect^.Left
else
UnderLineLen := gdk_text_width(UseFont,LineStart, LineLen);
Y := TxtPt.Y + 1;
gdk_draw_line(Buffer, DevCtx.GC, TxtPt.X, Y, TxtPt.X+UnderLineLen, Y);
end;
{$IFDEF DebugGDKTraps}EndGDKErrorTrap;{$ENDIF}
end;
begin
//DebugLn(Format('trace:> [TGtkWidgetSet.ExtTextOut] DC:0x%x, X:%d, Y:%d, Options:%d, Str:''%s'', Count: %d', [DC, X, Y, Options, Str, Count]));
if not IsValidDC(DC) then Exit(False);
if DevCtx.HasTransf then
begin
DevCtx.TransfPoint(X, Y);
if Rect <> nil then
begin
Rect^ := DevCtx.TransfRectIndirect(Rect^);
DevCtx.TransfNormalize(Rect^.Left, Rect^.Right);
DevCtx.TransfNormalize(Rect^.Top, Rect^.Bottom);
end;
end;
if ((Options and (ETO_OPAQUE or ETO_CLIPPED)) <> 0)
and (Rect=nil)
then begin
DebugLn('WARNING: [TGtkWidgetSet.ExtTextOut] Rect=nil');
exit(False);
end;
// TODO: implement other parameters.
// to reduce flickering calculate first and then paint
DCOrigin := DevCtx.Offset;
buffered := false;
UseFont := nil;
buffer := DevCtx.Drawable;
UnderLine := false;
if (Str <> nil) and (Count>0)
then begin
Usefont := GetGtkFont(DevCtx);
if UseFont = nil
then begin
DebugLn('WARNING: [TGtkWidgetSet.ExtTextOut] Missing Font');
Exit(False);
end;
if (DevCtx.CurrentFont <> nil) and (DevCtx.CurrentFont^.GDIFontObject <> nil)
then UnderLine := (DevCtx.CurrentFont^.LogFont.lfUnderline <> 0);
if (Options and ETO_CLIPPED) <> 0
then begin
X := Rect^.Left;
Y := Rect^.Top;
IntersectClipRect(DC, Rect^.Left, Rect^.Top, Rect^.Right, Rect^.Bottom);
end;
end;
if ((Options and ETO_OPAQUE) <> 0)
then begin
Width := Rect^.Right - Rect^.Left;
Height := Rect^.Bottom - Rect^.Top;
DevCtx.SelectedColors := dcscCustom;
EnsureGCColor(DC, dccCurrentBackColor, True, False);
if buffered
then begin
Left:=0;
Top:=0;
end
else begin
Left:=Rect^.Left+DCOrigin.X;
Top:=Rect^.Top+DCOrigin.Y;
end;
{$IFDEF DebugGDKTraps}BeginGDKErrorTrap;{$ENDIF}
if IsBackgroundColor(TColor(DevCtx.CurrentBackColor.ColorRef))
then
StyleFillRectangle(buffer, DevCtx.GC, DevCtx.CurrentBackColor.ColorRef,
Left, Top, Width, Height)
else
gdk_draw_rectangle(buffer, DevCtx.GC, 1, Left, Top, Width, Height);
{$IFDEF DebugGDKTraps}EndGDKErrorTrap;{$ENDIF}
end;
if UseFont = nil then Exit(True);
UpdateDCTextMetric(DevCtx);
LineHeight := GetTextHeight(DevCtx.DCTextMetric);
if Buffered
then begin
TxtPt.X := 0;
TxtPt.Y := LineHeight;
end
else begin
TopY := Y;
TxtPt.X := X + DCOrigin.X;
TxtPt.Y := TopY + LineHeight + DCOrigin.Y;
end;
DevCtx.SelectTextProps;
LineStart:= Str;
LineLen := FindChar(#10,Str,Count);
if LineLen < 0
then begin
LineLen:=Count;
if Count > 0 then DrawTextLine;
Exit(True);
end;
//write multiple lines
StrEnd := Str+Count;
repeat
LineEnd := LineStart + LineLen;
if LineLen > 0 then DrawTextLine;
inc(TxtPt.Y, LineHeight);
LineStart := LineEnd + 1; // skip #10
if (LineStart<StrEnd) and (LineStart^=#13)
then Inc(LineStart); // skip #13
Count := StrEnd-LineStart;
LineLen:=FindChar(#10,LineStart,Count);
if LineLen < 0
then LineLen := Count;
until LineStart >= StrEnd;
//DebugLn(Format('trace:< [TGtkWidgetSet.ExtTextOut] DC:0x%x, X:%d, Y:%d, Options:%d, Str:''%s'', Count: %d', [DC, X, Y, Options, Str, Count]));
end;
{$EndIf}
{------------------------------------------------------------------------------
Function: FillRect
Params: none
Returns: Nothing
The FillRect function fills a rectangle by using the specified brush.
This function includes the left and top borders, but excludes the right and
bottom borders of the rectangle.
------------------------------------------------------------------------------}
function TGtkWidgetSet.FillRect(DC: HDC; const Rect: TRect; Brush: HBRUSH): Boolean;
begin
Result := IsValidDC(DC) and IsValidGDIObject(Brush);
if not Result or IsRectEmpty(Rect) then
exit;
Result := TGtkDeviceContext(DC).FillRect(Rect, Brush, True);
//DebugLn(Format('trace:< [TGtkWidgetSet.FillRect] DC:0x%x; Rect: ((%d,%d)(%d,%d)); brush: %x', [Integer(DC), Rect.left, rect.top, rect.right, rect.bottom, brush]));
end;
{------------------------------------------------------------------------------
Function: FillRgn
Params: DC: HDC; RegionHnd: HRGN; hbr: HBRUSH
Returns: True if the function succeeds
Fills a region by using the specified brush
------------------------------------------------------------------------------}
function TGtkWidgetSet.FillRgn(DC: HDC; RegionHnd: HRGN; hbr: HBRUSH): Bool;
var
GtkDC: Integer;
OldRgn: PGdkRegion;
DevCtx: TGtkDeviceContext absolute DC;
ARect: TRect;
CRect : TGDKRectangle;
hasClipping: Boolean;
begin
//todo: sanity checks for valid handle etc.
Result := IsValidDC(DC) and IsValidGDIObject(hbr) and IsValidGDIObject(RegionHnd);
if not Result then Exit;
GtkDC := SaveDC(DC);
DevCtx.ClipRegion := PGdiObject(CreateRegionCopy(RegionHnd));
OldRgn:= DevCtx.ClipRegion^.GDIRegionObject;
hasClipping := Assigned(OldRgn); //todo: Check a better way
try
if hasClipping then
if SelectClipRGN(DC, RegionHnd) <> ERROR then
begin
gdk_region_get_clipbox(PGDIObject(RegionHnd)^.GDIRegionObject, @CRect);
ARect := RectFromGdkRect(CRect);
DevCtx.FillRect(ARect, hbr, True);
if hasClipping then
SelectClipRGN(DC, HRGN(OldRgn));
Result := True;
end;
finally
if hasClipping then
gdk_region_destroy(OldRgn);
RestoreDC(DC, GtkDC);
end;
end;
{------------------------------------------------------------------------------
Function: Frame3d
Params: -
Returns: Nothing
Draws a 3d border in GTK native style.
------------------------------------------------------------------------------}
function TGtkWidgetSet.Frame3d(DC: HDC; var ARect: TRect;
const FrameWidth: integer; const Style: TBevelCut): boolean;
var
DevCtx: TGtkDeviceContext absolute DC;
TheStyle: PGtkStyle;
i, AWidth: integer;
P: TPoint;
gc1, gc2: PGdkGC;
OldGC1Values, OldGC2Values: TGdkGCValues;
begin
Result := IsValidDC(DC);
if not Result or (FrameWidth = 0) then Exit;
TheStyle := gtk_widget_get_style(GetStyleWidget(lgsButton));
if TheStyle = nil then exit;
if DevCtx.HasTransf then
begin
ARect := DevCtx.TransfRectIndirect(ARect);
DevCtx.TransfNormalize(ARect.Left, ARect.Right);
DevCtx.TransfNormalize(ARect.Top, ARect.Bottom);
P.X := FrameWidth;
P.Y := FrameWidth;
P := DevCtx.TransfExtentIndirect(P);
AWidth := Abs(Min(P.X, P.Y));
end else
AWidth := FrameWidth;
case Style of
bvNone:
begin
InflateRect(ARect, -AWidth, -AWidth);
Exit;
end;
bvLowered:
begin
gc1 := TheStyle^.dark_gc[GTK_STATE_NORMAL];
gc2 := TheStyle^.light_gc[GTK_STATE_NORMAL];
end;
bvRaised:
begin
gc1 := TheStyle^.light_gc[GTK_STATE_NORMAL];
gc2 := TheStyle^.dark_gc[GTK_STATE_NORMAL];
end;
bvSpace:
begin
InflateRect(ARect, -AWidth, -AWidth);
Exit;
end;
end;
with DevCtx do
begin
if WithChildWindows then
begin
gdk_gc_get_values(gc1, @OldGC1Values);
gdk_gc_get_values(gc2, @OldGC2Values);
gdk_gc_set_subwindow(gc1, GDK_INCLUDE_INFERIORS);
gdk_gc_set_subwindow(gc2, GDK_INCLUDE_INFERIORS);
end;
for i := 1 to AWidth do
begin
gdk_draw_line(Drawable, gc1, ARect.Left + Offset.x, ARect.Top + Offset.y,
ARect.Right + Offset.x - 2, ARect.Top + Offset.y);
gdk_draw_line(Drawable, gc1, ARect.Left + Offset.x, ARect.Top + Offset.y,
ARect.Left + Offset.x, ARect.Bottom + Offset.y - 2);
gdk_draw_line(Drawable, gc2, ARect.Left + Offset.x, ARect.Bottom + Offset.y - 1,
ARect.Right + Offset.x - 1, ARect.Bottom + Offset.y - 1);
gdk_draw_line(Drawable, gc2, ARect.Right + Offset.x - 1, ARect.Top + Offset.y,
ARect.Right + Offset.x - 1, ARect.Bottom + Offset.y - 1);
// inflate the rectangle (! ARect will be returned to the user with this)
InflateRect(ARect, -1, -1);
end;
if WithChildWindows then
begin
gdk_gc_set_subwindow(gc1, OldGC1Values.subwindow_mode);
gdk_gc_set_subwindow(gc2, OldGC2Values.subwindow_mode);
end;
end;
end;
{------------------------------------------------------------------------------
function TGtkWidgetSet.FrameRect(DC: HDC; const ARect: TRect;
hBr: HBRUSH): Integer;
------------------------------------------------------------------------------}
function TGtkWidgetSet.FrameRect(DC: HDC; const ARect: TRect;
hBr: HBRUSH): Integer;
var
DevCtx: TGtkDeviceContext absolute DC;
DCOrigin: TPoint;
R: TRect;
begin
Result:=0;
if not IsValidDC(DC) then Exit;
if not IsValidGDIObject(hBr) then Exit;
// Draw outline
Result := 1;
if PGdiObject(hBr)^.IsNullBrush then Exit;
DevCtx.SelectedColors:= dcscCustom;
EnsureGCColor(DC, dccGDIBrushColor, True, False);//Brush Color
if DevCtx.HasTransf then
begin
R := DevCtx.TransfRectIndirect(ARect);
DevCtx.TransfNormalize(R.Left, R.Right);
DevCtx.TransfNormalize(R.Top, R.Bottom);
end else
R := ARect;
DCOrigin := DevCtx.Offset;
gdk_draw_rectangle(DevCtx.Drawable, DevCtx.GC, 0,
R.Left+DCOrigin.X, R.Top+DCOrigin.Y,
R.Right-R.Left-1, R.Bottom-R.Top-1);
end;
{------------------------------------------------------------------------------
Function: GetActiveWindow
Params: none
Returns:
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetActiveWindow : HWND;
var
TopList, List: PGList;
Widget: PGTKWidget;
Window: PGTKWindow;
begin
// Default to 0
Result := 0;
TopList := gdk_window_get_toplevels;
List := TopList;
while List <> nil do
begin
if (List^.Data <> nil)
then begin
gdk_window_get_user_data(PGDKWindow(List^.Data), Pgpointer(@Window));
if gtk_is_window(Window)
then begin
Widget := Window^.focus_widget;
if Widget=nil then Widget:=PGtkWidget(Window);
//DebugLn('TGtkWidgetSet.GetActiveWindow Window=',GetWidgetDebugReport(PgtkWidget(Window)),' Window^.focus_widget= ',GetWidgetDebugReport(Window^.focus_widget));
if (Widget <> nil) and gtk_widget_has_focus(Widget)
then begin
// return the window
Result := HWND(PtrUInt(GetMainWidget(PGtkWidget(Window))));
//DebugLn('TGtkWidgetSet.GetActiveWindow Result=',GetWidgetDebugReport(PgtkWidget(Result)));
Break;
end;
end;
end;
list := g_list_next(list);
end;
if TopList <> nil
then g_list_free(TopList);
end;
{------------------------------------------------------------------------------
Function: GetDIBits
Params:
Returns:
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetDIBits(DC: HDC; Bitmap: HBitmap; StartScan, NumScans: UINT;
Bits: Pointer; var BitInfo: BitmapInfo; Usage: UINT): Integer;
begin
//DebugLn('trace:[TGtkWidgetSet.GetDIBits]');
Result := 0;
if IsValidGDIObject(Bitmap)
then begin
case PGDIObject(Bitmap)^.GDIType of
gdiBitmap:
Result := InternalGetDIBits(DC, Bitmap, StartScan, NumScans, -1, Bits,
BitInfo, Usage, True);
else
DebugLn('WARNING: [TGtkWidgetSet.GetDIBits] not a Bitmap!');
end;
end
else
DebugLn('WARNING: [TGtkWidgetSet.GetDIBits] invalid Bitmap!');
end;
{------------------------------------------------------------------------------
Function: GetBitmapBits
Params:
Returns:
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetBitmapBits(Bitmap: HBITMAP; Count: Longint; Bits: Pointer): Longint;
var
BitInfo : tagBitmapInfo;
begin
//DebugLn('trace:[TGtkWidgetSet.GetBitmapBits]');
Result := 0;
if IsValidGDIObject(Bitmap)
then begin
case PGDIObject(Bitmap)^.GDIType of
gdiBitmap:
Result := InternalGetDIBits(0, Bitmap, 0, 0, Count, Bits, BitInfo, 0, False);
else
DebugLn('WARNING: [TGtkWidgetSet.GetBitmapBits] not a Bitmap!');
end;
end
else
DebugLn('WARNING: [TGtkWidgetSet.GetBitmapBits] invalid Bitmap!');
end;
{------------------------------------------------------------------------------
Function: GetCapture
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetCapture: HWND;
var
Widget: PGtkWidget;
AWindow: PGtkWindow;
IsModal: gboolean;
begin
Widget:=gtk_grab_get_current;
// for the LCL a modal window is not capturing
if Widget<>nil then begin
if GtkWidgetIsA(Widget,GTK_TYPE_WINDOW) then begin
AWindow:=PGtkWindow(Widget);
IsModal:=gtk_window_get_modal(AWindow);
if IsModal then
Widget:=nil;
end;
end;
Result := HWnd(PtrUInt(Widget));
end;
{------------------------------------------------------------------------------
Function: GetCaretPos
Params: lpPoint: The caretposition
Returns: True if succesful
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetCaretPos(var lpPoint: TPoint): Boolean;
var
//FocusObject: PGTKObject;
modmask : TGDKModifierType;
begin
{$IFDEF DebugGDKTraps}
BeginGDKErrorTrap;
{$ENDIF}
gdk_window_get_pointer(nil,@lpPoint.X,@lpPoint.y,@modmask);
{$IFDEF DebugGDKTraps}
EndGDKErrorTrap;
{$ENDIF}
Result := True;
end;
{------------------------------------------------------------------------------
function TGtkWidgetSet.GetCaretRespondToFocus(handle: HWND;
var ShowHideOnFocus: boolean): Boolean;
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetCaretRespondToFocus(handle: HWND;
var ShowHideOnFocus: boolean): Boolean;
begin
if handle<>0 then begin
if gtk_type_is_a(gtk_object_type(PGTKObject(handle)), GTKAPIWidget_GetType)
then begin
GTKAPIWidget_GetCaretRespondToFocus(PGTKAPIWidget(handle),
ShowHideOnFocus);
Result:=true;
end
else begin
Result := False;
end;
end else
Result:=false;
end;
{------------------------------------------------------------------------------
Function: GetCharABCWidths pbd
Params: Don't care yet
Returns: False so that the font cache in the newest mwEdit will use
TextMetrics info which is working already
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetCharABCWidths(DC: HDC; p2, p3: UINT;
const ABCStructs): Boolean;
begin
Result := False;
end;
{------------------------------------------------------------------------------
Function: GetClientBounds
Params: handle:
Result:
Returns: true on success
Returns the client bounds of a control. The client bounds is the rectangle of
the inner area of a control, where the child controls are visible. The
coordinates are relative to the control's left and top.
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetClientBounds(handle : HWND; var ARect : TRect) : Boolean;
var
Widget, ClientWidget: PGtkWidget;
{$IFDEF Gtk1}
MainOrigin: TPoint;
{$ELSE}
CurGDKWindow: PGdkWindow;
{$ENDIF}
ClientOrigin: TPoint;
ClientWindow, MainWindow: PGdkWindow;
begin
Result := False;
if Handle = 0 then Exit;
Widget := pgtkwidget(Handle);
ClientWidget := GetFixedWidget(Widget);
if (ClientWidget <> Widget) then begin
ClientWindow:=GetControlWindow(ClientWidget);
MainWindow:=GetControlWindow(Widget);
if MainWindow<>ClientWindow then begin
// widget and client are on different gdk windows
{$IFDEF Gtk1}
if MainWindow<>nil then begin
gdk_window_get_origin(MainWindow,@MainOrigin.X,@MainOrigin.Y);
end else begin
// widget not realized
MainOrigin.X:=0;
MainOrigin.Y:=0;
end;
// check if the main gdkwindow is the clientwindow of the parent
if (Widget^.Parent<>nil)
and (MainWindow=gtk_widget_get_parent_window(Widget)) then begin
// the widget is using its parent window
// -> adjust the coordinates
inc(MainOrigin.X,Widget^.Allocation.X);
inc(MainOrigin.Y,Widget^.Allocation.Y);
end;
if ClientWindow<>nil then begin
gdk_window_get_origin(ClientWindow,@ClientOrigin.X,@ClientOrigin.Y);
end else begin
// client widget not realized
ClientOrigin:=MainOrigin;
end;
ARect.Left:=ClientOrigin.X-MainOrigin.X;
ARect.Top:=ClientOrigin.Y-MainOrigin.Y;
{$ELSE}
if (GTK_WIDGET_NO_WINDOW(ClientWidget)) then begin
// ClientWidget is a sub widget
ARect.Left:=ClientWidget^.allocation.x;
ARect.Top:=ClientWidget^.allocation.y;
end else begin
// ClientWidget owns the gdkwindow
ARect.Left:=0;
ARect.Top:=0;
end;
CurGDKWindow:=ClientWindow;
while (CurGDKWindow<>MainWindow) do begin
gdk_window_get_position(CurGDKWindow,@ClientOrigin.x,@ClientOrigin.y);
inc(ARect.Left,ClientOrigin.x);
inc(ARect.Top,ClientOrigin.y);
CurGDKWindow:=gdk_window_get_parent(CurGDKWindow);
end;
if GTK_WIDGET_NO_WINDOW(Widget) then begin
// Widget is a sub widget
dec(ARect.Left,Widget^.allocation.x);
dec(ARect.Top,Widget^.allocation.y);
end;
{$ENDIF}
ARect.Right:=ARect.Left+ClientWidget^.Allocation.Width;
ARect.Bottom:=ARect.Top+ClientWidget^.Allocation.Height;
Result:=true;
end else if MainWindow<>nil then begin
// both are on the same gdkwindow
ARect.Left:=ClientWidget^.allocation.X-Widget^.allocation.X;
ARect.Top:=ClientWidget^.allocation.Y-Widget^.allocation.Y;
ARect.Right:=ARect.Left+ClientWidget^.allocation.Width;
ARect.Bottom:=ARect.Top+ClientWidget^.allocation.Height;
Result:=true;
end;
end;
if not Result then begin
with Widget^.Allocation do
ARect := Rect(0,0,Width,Height);
end;
Result:=true;
end;
{------------------------------------------------------------------------------
Function: GetClientRect
Params: handle:
Result:
Returns: true on success
Returns the client rectangle of a control. Left and Top are always 0.
The client rectangle is the size of the inner area of a control, where the
child controls are visible.
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetClientRect(handle : HWND; var ARect : TRect) : Boolean;
var
Widget, ClientWidget: PGtkWidget;
procedure GetNoteBookClientRect(NBWidget: PGtkNotebook);
var
PageIndex: LongInt;
PageWidget: PGtkWidget;
FrameBorders: TRect;
aWidth: LongInt;
aHeight: LongInt;
begin
// get current page
PageIndex:=gtk_notebook_get_current_page(NBWidget);
if PageIndex>=0 then
PageWidget:=gtk_notebook_get_nth_page(NBWidget,PageIndex)
else
PageWidget:=nil;
if (PageWidget<>nil) and GTK_WIDGET_RC_STYLE(PageWidget)
and ((PageWidget^.Allocation.Width>1) or (PageWidget^.Allocation.Height>1))
then begin
// get the size of the current page
ARect.Right:=PageWidget^.Allocation.Width;
ARect.Bottom:=PageWidget^.Allocation.Height;
//DebugLn(['GetNoteBookClientRect using pagewidget: ',GetWidgetDebugReport(Widget),' ARect=',dbgs(aRect)]);
end else begin
// use defaults
FrameBorders:=GetStyleNotebookFrameBorders;
aWidth:=Widget^.allocation.width;
aHeight:=Widget^.allocation.height;
ARect:=Rect(0,0,
Max(0,AWidth-FrameBorders.Left-FrameBorders.Right),
Max(0,aHeight-FrameBorders.Top-FrameBorders.Bottom));
//DebugLn(['GetNoteBookClientRect using defaults: ',GetWidgetDebugReport(Widget),' ARect=',dbgs(aRect)]);
end;
end;
begin
Result := false;
if Handle = 0 then Exit;
ARect.Left := 0;
ARect.Top := 0;
Widget := PGtkWidget(Handle);
ClientWidget := GetFixedWidget(Widget);
if (ClientWidget <> nil) then
Widget := ClientWidget;
if (Widget <> nil) then begin
ARect.Right:=Widget^.Allocation.Width;
ARect.Bottom:=Widget^.Allocation.Height;
if GtkWidgetIsA(Widget,gtk_notebook_get_type) then
GetNoteBookClientRect(PGtkNoteBook(Widget));
end else begin
ARect.Right:=0;
ARect.Bottom:=0;
end;
{$IfDef VerboseGetClientRect}
if ClientWidget<>nil then begin
DebugLn('GetClientRect Widget=',GetWidgetDebugReport(PgtkWidget(Handle)),
' Client=',DbgS(ClientWidget),WidgetFlagsToString(ClientWidget),
' WindowSize=',dbgs(ARect.Right),',',dbgs(ARect.Bottom),
' Allocation=',dbgs(ClientWidget^.Allocation.Width),',',dbgs(ClientWidget^.Allocation.Height)
);
end else begin
DebugLn('GetClientRect Widget=',GetWidgetDebugReport(PgtkWidget(Handle)),
' Client=',DbgS(ClientWidget),WidgetFlagsToString(ClientWidget),
' WindowSize=',dbgs(ARect.Right),',',dbgs(ARect.Bottom),
' Allocation=',dbgs(Widget^.Allocation.Width),',',dbgs(Widget^.Allocation.Height)
);
end;
if GetLCLObject(Widget) is TCustomPage then begin
DebugLn(['TGtkWidgetSet.GetClientRect Rect=',dbgs(aRect),' ',GetWidgetDebugReport(Widget)]);
end;
{$EndIf}
Result:=true;
end;
{------------------------------------------------------------------------------
Function: GetClipBox
Params: dc, lprect
Returns: Integer
Returns the smallest rectangle which includes the entire current
Clipping Region, or if no Clipping Region is set, the current
dimensions of the Drawable.
The result can be one of the following constants
Error
NullRegion
SimpleRegion
ComplexRegion
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetClipBox(DC : hDC; lpRect : PRect) : Longint;
var
DevCtx: TGtkDeviceContext absolute DC;
CRect : TGDKRectangle;
X, Y : Longint;
DCOrigin: Tpoint;
begin
// set default values
Result := SIMPLEREGION;
if lpRect <> nil then
lpRect^ := Rect(0,0,0,0);
if not IsValidDC(DC)
then begin
Result := ERROR;
Exit;
end;
DCOrigin := DevCtx.Offset;
if DevCtx.ClipRegion = nil
then begin
if (DevCtx.PaintRectangle.Left<>0)
or (DevCtx.PaintRectangle.Top<>0)
or (DevCtx.PaintRectangle.Right<>0)
or (DevCtx.PaintRectangle.Bottom<>0) then begin
lpRect^:=DevCtx.PaintRectangle;
end else begin
gdk_window_get_size(DevCtx.Drawable, @X, @Y);
lpRect^ := Rect(0,0,X,Y);
end;
OffsetRect(lpRect^,-DCOrigin.X, -DCOrigin.Y);
Result := SIMPLEREGION;
end
else begin
Result := RegionType(DevCtx.ClipRegion^.GDIRegionObject);
gdk_region_get_clipbox(DevCtx.ClipRegion^.GDIRegionObject, @CRect);
lpRect^.Left := CRect.X-DCOrigin.X;
lpRect^.Top := CRect.Y-DCOrigin.Y;
lpRect^.Right := lpRect^.Left + CRect.Width;
lpRect^.Bottom := lpRect^.Top + CRect.Height;
end;
end;
{------------------------------------------------------------------------------
Function: GetRGNBox
Params: rgn, lprect
Returns: Integer
Returns the smallest rectangle which includes the entire passed
Region, if lprect is null then just returns RegionType.
The result can be one of the following constants
Error
NullRegion
SimpleRegion
ComplexRegion
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetRgnBox(RGN : HRGN; lpRect : PRect) : Longint;
var
CRect : TGDKRectangle;
begin
Result := SIMPLEREGION;
If lpRect <> nil then
lpRect^ := Rect(0,0,0,0);
If Not IsValidGDIObject(RGN) then
Result := ERROR
else begin
Result := RegionType(PGDIObject(RGN)^.GDIRegionObject);
If lpRect <> nil then begin
gdk_region_get_clipbox(PGDIObject(RGN)^.GDIRegionObject,
@CRect);
With lpRect^,CRect do begin
Left := X;
Top := Y;
Right := X + Width;
Bottom := Y + Height;
end;
end;
end;
end;
function TGtkWidgetSet.GetROP2(DC: HDC): Integer;
begin
if IsValidDC(DC)
then Result := TGtkDeviceContext(DC).ROP2
else Result := 0;
end;
{------------------------------------------------------------------------------
Function: GetClipRGN
Params: dc, rgn
Returns: Integer
Returns a copy of the current Clipping Region.
The result can be one of the following constants
0 = no clipping set
1 = ok
-1 = error
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetClipRGN(DC : hDC; RGN : hRGN) : longint;
var
DCOrigin: TPoint;
ClipRegionWithDCOffset: PGdkRegion;
CurRegionObject: PGdkRegion;
ARect: TRect;
begin
Result := SIMPLEREGION;
If (not IsValidDC(DC)) then
Result := ERROR
else If Not IsValidGDIObject(RGN) then begin
Result := ERROR;
DebugLn('WARNING: [TGtkWidgetSet.GetClipRGN] Invalid HRGN');
end
else if (TGtkDeviceContext(DC).ClipRegion<>nil)
and (not IsValidGDIObject(HGDIOBJ(PtrUInt(TGtkDeviceContext(DC).ClipRegion)))) then
Result := ERROR
else with TGtkDeviceContext(DC) do
begin
CurRegionObject:=nil;
if ClipRegion<>nil then
CurRegionObject:=ClipRegion^.GDIRegionObject;
ARect:=Rect(0,0,0,0);
if CurRegionObject<>nil then begin
// create a copy of the current clipregion
ClipRegionWithDCOffset:=gdk_region_copy(CurRegionObject);
// move it to the DC offset
// Example: if the ClipRegion is at 10,10 and the DCOrigin is at 10,10,
// then the ClipRegion must be moved to 0,0
DCOrigin := Offset;
//debugln('TGtkWidgetSet.GetClipRGN DCOrigin=',dbgs(DCOrigin),' CurRegionObject=',dbgs(CurRegionObject),' ',dbgs(ARect));
gdk_region_offset(ClipRegionWithDCOffset,-DCOrigin.x,-DCOrigin.Y);
end else begin
// create a default clipregion
GetClipBox(DC,@ARect);
ClipRegionWithDCOffset:=CreateRectGDKRegion(ARect);
end;
// free the old region in RGN
if PGdiObject(RGN)^.GDIRegionObject<>nil then
gdk_region_destroy(PGdiObject(RGN)^.GDIRegionObject);
// set the new region in RGN
PGdiObject(RGN)^.GDIRegionObject := ClipRegionWithDCOffset;
Result := RegionType(ClipRegionWithDCOffset);
//DebugLn('TGtkWidgetSet.GetClipRGN B DC=',DbgS(DC),
// ' DCOrigin=',dbgs(DCOrigin),' RGN=',GDKRegionAsString(ClipRegionWithDCOffset),' Result=',dbgs(Result));
If Result = NULLREGION then
Result := 0
else If Result <> ERROR then
Result := 1;
end;
If Result = ERROR then
Result := -1;
end;
{------------------------------------------------------------------------------
Function: GetCmdLineParamDescForInterface
Params: none
Returns: ansistring
Returns a description of the command line parameters, that are understood by
the interface.
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetCmdLineParamDescForInterface: string;
function b(const s: string): string;
begin
Result:=BreakString(s,75,22)+LineEnding+LineEnding;
end;
begin
Result:=
b(rsgtkOptionNoTransient)
+b(rsgtkOptionModule)
+b(rsgOptionFatalWarnings)
+b(rsgtkOptionDebug)
+b(rsgtkOptionNoDebug)
+b(rsgdkOptionDebug)
+b(rsgdkOptionNoDebug)
+b(rsgtkOptionDisplay)
+b(rsgtkOptionSync)
+b(rsgtkOptionNoXshm)
+b(rsgtkOptionName)
+b(rsgtkOptionClass);
end;
{------------------------------------------------------------------------------
Function: GetCursorPos
Params: lpPoint: The cursorposition
Returns: True if succesful
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetCursorPos(var lpPoint: TPoint): Boolean;
{$IFDEF HasX}
var
dpy: PDisplay;
root, child: twindow;
winx, winy: Integer;
xmask: Cardinal;
begin
Result := true;
if (not MousePositionValid) or (Abs(MousePositionTime-Now)>1/864000) then
begin
// querying the X cursor is expensive (especially on network connections)
// => use a lazy query
{$IFDEF DebugGDKTraps}
BeginGDKErrorTrap;
try
{$ENDIF}
dpy := gdk_display;
XQueryPointer(dpy, RootWindow(dpy, DefaultScreen(dpy)), @root, @child,
@MousePosition.X,@MousePosition.Y,@winx,@winy,@xmask);
{$IFDEF DebugGDKTraps}
finally
EndGDKErrorTrap;
end;
{$ENDIF}
MousePositionTime:=Now;
MousePositionValid:=true;
end;
lpPoint:=MousePosition;
end;
{$ELSE}
begin
// TODO: GTK1-win32 GetCursorPos
Result := False;
end;
{$ENDIF HasX}
function TGTKWidgetSet.GetCurrentObject(DC: HDC; uObjectType: UINT): HGDIOBJ;
var
GtkDC: TGtkDeviceContext absolute DC;
begin
Result := 0;
if not GTKWidgetSet.IsValidDC(DC) then
Exit;
case uObjectType of
OBJ_BITMAP: Result := HGDIOBJ(GtkDC.CurrentBitmap);
OBJ_BRUSH: Result := HGDIOBJ(GtkDC.CurrentBrush);
OBJ_FONT: Result := HGDIOBJ(GtkDC.CurrentFont);
OBJ_PEN: Result := HGDIOBJ(GtkDC.CurrentPen);
end;
end;
{------------------------------------------------------------------------------
Function: GetDC
Params: none
Returns: Nothing
hWnd is any widget.
The DC will be created for the client area and without the child areas
(they are clipped away). Child areas are all child gdkwindows
(e.g. not TControls).
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetDC(hWnd: HWND): HDC;
begin
Result:=CreateDCForWidget(PGtkWidget(hWnd),nil,false);
end;
{------------------------------------------------------------------------------
function TGtkWidgetSet.GetDeviceCaps(DC: HDC; Index: Integer): Integer;
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetDeviceCaps(DC: HDC; Index: Integer): Integer;
var
Visual: PGdkVisual;
function GetVisual: boolean;
begin
Visual:=nil;
with TGtkDeviceContext(DC) do begin
If Drawable <> nil then
Visual:=gdk_window_get_visual(PGdkWindow(Drawable));
if Visual = nil then
Visual := GDK_Visual_Get_System;
end;
Result:=Visual<>nil;
end;
begin
Result := -1;
If DC = 0 then begin
DC := GetDC(0);
If DC = 0 then
exit;
Result := GetDeviceCaps(DC, Index);
ReleaseDC(0, DC);
exit;
end;
if not IsValidDC(DC) then exit;
with TGtkDeviceContext(DC) do
Case Index of
HORZRES : { Horizontal width in pixels }
If Drawable = nil then
Result := GetSystemMetrics(SM_CXSCREEN)
else
gdk_drawable_get_size(Drawable, @Result, nil);
VERTRES : { Vertical height in pixels }
If Drawable = nil then
Result := GetSystemMetrics(SM_CYSCREEN)
else
gdk_drawable_get_size(Drawable, nil, @Result);
BITSPIXEL : { Number of used bits per pixel = depth }
If Drawable = nil then
Result := GDK_Visual_Get_System^.Depth
else
Result := gdk_drawable_get_depth(Drawable);
PLANES : { Number of planes }
// ToDo
Result := 1;
//For Size in MM, MM = (Pixels*100)/(PPI*25.4)
HORZSIZE : { Horizontal size in millimeters }
Result := RoundToInt((GetDeviceCaps(DC, HORZRES) * 100) /
(GetDeviceCaps(DC, LOGPIXELSX) * 25.4));
VERTSIZE : { Vertical size in millimeters }
Result := RoundToInt((GetDeviceCaps(DC, VERTRES) * 100) /
(GetDeviceCaps(DC, LOGPIXELSY) * 25.4));
//So long as gdk_screen_width_mm is acurate, these should be
//acurate for Screen GDKDrawables. Once we get Metafiles
//we will also have to add internal support for Papersizes etc..
LOGPIXELSX : { Logical pixels per inch in X }
Result := RoundToInt(gdk_screen_width / (GetScreenWidthMM / 25.4));
LOGPIXELSY : { Logical pixels per inch in Y }
Result := RoundToInt(gdk_screen_height / (GetScreenHeightMM / 25.4));
SIZEPALETTE: { number of entries in color palette }
if GetVisual then
Result:=Visual^.colormap_size
else
Result:=0;
NUMRESERVED: { number of reserverd colors in color palette }
Result:=0;
else
DebugLn('TGtkWidgetSet.GetDeviceCaps not supported: Type=',dbgs(Index));
end;
end;
{------------------------------------------------------------------------------
function GetDeviceSize(DC: HDC; var p: TPoint): boolean;
Retrieves the width and height of the device context in pixels.
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetDeviceSize(DC: HDC; var p: TPoint): boolean;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
if not IsValidDC(DC) then Exit(False);
if DevCtx.Drawable <> nil
then begin
P := Point(0,0);
gdk_window_get_size(PGdkWindow(DevCtx.Drawable), @P.X, @P.Y);
Exit(True);
end;
{$ifdef gtk1}
if DevCtx.Widget = nil
then begin
// either empty or gtk1screen
p.x:=gdk_screen_width;
p.y:=gdk_screen_height;
Exit(True);
end;
{$endif}
{$IFDEF RaiseExceptionOnNilPointers}
RaiseException('TGtkWidgetSet.GetDeviceSize Window=nil');
{$ENDIF}
DebugLn('TGtkWidgetSet.GetDeviceSize:', ' WARNING: DC ',DbgS(DC),' without gdkwindow.',
' Widget=',DbgS(DevCtx.Widget));
Result := False;
end;
{------------------------------------------------------------------------------
function TGtkWidgetSet.GetDCOriginRelativeToWindow(PaintDC: HDC;
WindowHandle: HWND; var OriginDiff: TPoint): boolean;
Returns the origin of PaintDC relative to the window handle.
Example:
A PaintDC of a TButton at 20,10 with a DC Offset of 0,0 on a form and the
WindowHandle is the form.
Then OriginDiff is the difference between the Forms client origin
and the PaintDC: 20,10.
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetDCOriginRelativeToWindow(PaintDC: HDC;
WindowHandle: HWND; var OriginDiff: TPoint): boolean;
var
DevCtx: TGtkDeviceContext absolute PaintDC;
DCOrigin: TPoint;
DCScreenOrigin: TPoint;
WindowScreenOrigin: TPoint;
Widget: PGtkWidget;
DCWindow: PGdkWindow;
begin
Result := false;
OriginDiff := Point(0,0);
if not IsValidDC(PaintDC) then exit;
DCOrigin := DevCtx.Offset;
DCWindow:=PGdkWindow(DevCtx.Drawable);
gdk_window_get_origin(DCWindow, @(DCScreenOrigin.X), @(DCScreenOrigin.Y));
inc(DCScreenOrigin.X, DCOrigin.X);
inc(DCScreenOrigin.Y, DCOrigin.Y);
Widget := GetFixedWidget(PGtkWidget(WindowHandle));
if Widget = nil
then Widget := PGtkWidget(WindowHandle);
gdk_window_get_origin(PGdkWindow(Widget^.window), @(WindowScreenOrigin.X), @(WindowScreenOrigin.Y));
OriginDiff.X := DCScreenOrigin.X-WindowScreenOrigin.X;
OriginDiff.Y := DCScreenOrigin.Y-WindowScreenOrigin.Y;
Result := true;
//DebugLn(['TGtkWidgetSet.GetDCOriginRelativeToWindow DCScreenOrigin=',dbgs(DCScreenOrigin),' WindowScreenOrigin=',dbgs(WindowScreenOrigin),' OriginDiff=',dbgs(OriginDiff)]);
end;
{------------------------------------------------------------------------------
Function: GetDesignerDC
Params: none
Returns: Nothing
WindowHandle is any widget.
The DC will be created for the client area including the child areas.
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetDesignerDC(WindowHandle: HWND): HDC;
begin
//DebugLn('TGtkWidgetSet.GetDesignerDC A');
Result:=CreateDCForWidget(PGtkWidget(WindowHandle),nil,true);
end;
{------------------------------------------------------------------------------
Function: GetFocus
Params: none
Returns: The handle of the window with focus
The GetFocus function retrieves the handle of the window that has the focus.
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetFocus: HWND;
var
TopList, List: PGList;
Widget: PGTKWidget;
Window: PGTKWindow;
Info: PWidgetInfo;
begin
// Default to 0
Result := 0;
{$IFDEF DebugGDKTraps}
BeginGDKErrorTrap;
{$ENDIF}
TopList := gdk_window_get_toplevels;
List := TopList;
while List <> nil do
begin
if (List^.Data <> nil)
then begin
gdk_window_get_user_data(PGDKWindow(List^.Data), Pgpointer(@Window));
if gtk_is_window(Window)
then begin
Widget := Window^.focus_widget;
{$IFDEF DebugLCLComponents}
if DebugGtkWidgets.IsDestroyed(Widget) then begin
DebugLn(['TGtkWidgetSet.GetFocus Window^.focus_widget was already destroyed:']);
DebugLn(DebugGtkWidgets.GetInfo(Widget,true));
end;
{$ENDIF}
if (Widget <> nil) and gtk_widget_has_focus(Widget)
then begin
Info:=GetWidgetInfo(PGtkWidget(Window),false);
if (Info=nil) or (not (wwiDeactivating in Info^.Flags)) then
Result := HWND(PtrUInt(GetMainWidget(Widget)));
Break;
end;
end;
end;
list := g_list_next(list);
end;
if TopList <> nil
then g_list_free(TopList);
{$IFDEF DebugGDKTraps}EndGDKErrorTrap;{$ENDIF}
end;
{------------------------------------------------------------------------------
function GetFontLanguageInfo(DC: HDC): DWord; override;
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetFontLanguageInfo(DC: HDC): DWord;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
Result := 0;
If IsValidDC(DC) then
with TGtkDeviceContext(DC) do begin
UpdateDCTextMetric(TGtkDeviceContext(DC));
if TGtkDeviceContext(DC).DCTextMetric.IsDoubleByteChar then
inc(Result,GCP_DBCS);
end;
end;
{------------------------------------------------------------------------------
Function: GetKeyState
Params: nVirtKey: The requested key
Returns: If the function succeeds, the return value specifies the status of
the given virtual key. If the high-order bit is 1, the key is down;
otherwise, it is up. If the low-order bit is 1, the key is toggled.
The GetKeyState function retrieves the status of the specified virtual key.
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetKeyState(nVirtKey: Integer): Smallint;
const
StateDown = -128; // $FF80
StateToggled = 1;
KEYSTATE: array[Boolean] of Smallint = (0, StateDown);
TOGGLESTATE: array[Boolean] of Smallint = (0, StateToggled);
GDK_BUTTON_MASKS: array[VK_LBUTTON..VK_XBUTTON2] of guint32 =
(
{ VK_LBUTTON } GDK_BUTTON1_MASK,
{ VK_RBUTTON } GDK_BUTTON3_MASK,
{ VK_CANCEL } 0,
{ VK_MBUTTON } GDK_BUTTON2_MASK,
{ VK_XBUTTON1 } GDK_BUTTON4_MASK,
{ VK_XBUTTON2 } GDK_BUTTON5_MASK
);
var
GdkModMask: TGdkModifierType;
x, y: gint;
{$IFDEF GTK1}
List: PGList;
{$ENDIF}
begin
case nVirtKey of
// remap
VK_LSHIFT: nVirtKey := VK_SHIFT;
VK_LCONTROL: nVirtKey := VK_CONTROL;
VK_LMENU: nVirtKey := VK_MENU;
end;
{$IFDEF Use_KeyStateList}
Result := KEYSTATE[FKeyStateList_.IndexOf(Pointer(PtrInt(nVirtKey))) >=0];
{$ELSE}
Implement this
{$ENDIF}
// try extended keys
if Result = 0
then begin
{$IFDEF Use_KeyStateList}
Result := KEYSTATE[FKeyStateList_.IndexOf(Pointer(PtrInt(nVirtKey or KEYMAP_EXTENDED))) >=0];
{$ELSE}
Implement this
{$ENDIF}
end;
{$IFDEF Use_KeyStateList}
// add toggle
Result := Result or TOGGLESTATE[FKeyStateList_.IndexOf(Pointer(
PtrInt(nVirtKey or KEYMAP_TOGGLE))) >=0];
{$IFDEF GTK2}
// If there are tons of new keyboard errors this is probably the cause
GdkModMask := gtk_accelerator_get_default_mod_mask;
if (Result and StateDown) <> 0 then
begin
if (nVirtKey = VK_CONTROL) and (GdkModMask and GDK_CONTROL_MASK = 0) then
Result := Result and not StateDown;
//if (nVirtKey = VK_SHIFT) and (GtkModMask and GDK_SHIFT_MASK = 0 then
// Result := Result and not StateDown;
end;
{$ENDIF}
{$ENDIF}
// Mouse buttons. Toggle state is not tracked
if nVirtKey in [VK_LBUTTON, VK_RBUTTON, VK_MBUTTON..VK_XBUTTON2] then
begin
{$ifdef gtk1}
List := gdk_window_get_toplevels;
if g_list_length(List) > 0 then
gdk_window_get_pointer(g_list_nth_data(List, 0), @x, @y, @GdkModMask)
else
GdkModMask := 0;
g_list_free(List);
{$else}
gdk_display_get_pointer(gdk_display_get_default, nil,
@x, @y, @GdkModMask);
{$endif}
Result := Result or KEYSTATE[GdkModMask and GDK_BUTTON_MASKS[nVirtKey] <> 0]
end;
//DebugLn(Format('Trace:[TGtkWidgetSet.GetKeyState] %d -> 0x%x', [nVirtKey, Result]));
end;
function TGtkWidgetSet.GetMapMode(DC: HDC): Integer;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
if IsValidDC(DC) then
Result := DevCtx.MapMode
else
Result := 0;
end;
function TGTKWidgetSet.GetMonitorInfo(Monitor: HMONITOR; lpmi: PMonitorInfo): Boolean;
{$IFDEF HasX}
var
x, y, w, h: gint;
{$ENDIF}
begin
Result := (lpmi <> nil) and (lpmi^.cbSize >= SizeOf(TMonitorInfo)) and (Monitor = 1);
if not Result then Exit;
lpmi^.rcMonitor := Bounds(0, 0, gdk_screen_width, gdk_screen_height);
{$IFDEF HasX}
if XGetWorkarea(x, y, w, h) <> -1 then
lpmi^.rcWork := Bounds(x, y, w, h)
else
{$ENDIF}
lpmi^.rcWork := lpmi^.rcMonitor;
lpmi^.dwFlags := MONITORINFOF_PRIMARY
end;
{------------------------------------------------------------------------------
Function: GetObject
Params: GDIObj - handle, BufSize - size of Buf argument, Buf - buffer
Returns: Size of buffer
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetObject(GDIObj: HGDIOBJ; BufSize: Integer; Buf: Pointer): Integer;
function GetObject_Bitmap: Integer;
var
NumColors, ImageDepth: Longint;
BitmapSection : TDIBSECTION;
begin
if Buf = nil
then begin
Result := SizeOf(TDIBSECTION);
Exit;
end;
Result := 0;
FillChar(BitmapSection, SizeOf(TDIBSECTION), 0);
with PGDIObject(GDIObj)^, BitmapSection,
BitmapSection.dsBm, BitmapSection.dsBmih
do begin
{dsBM - BITMAP}
bmType := LeToN($4D42);
bmWidth := 0 ;
bmHeight := 0;
{bmWidthBytes: Longint;}
bmPlanes := 1;//Does Bitmap Format support more?
bmBitsPixel := 1;
bmBits := nil;
{dsBmih - BITMAPINFOHEADER}
biSize := 40;
biWidth := 0;
biHeight := 0;
biPlanes := bmPlanes;
biBitCount := 1;
biCompression := 0;
biSizeImage := 0;
biXPelsPerMeter := 0;
biYPelsPerMeter := 0;
biClrUsed := 0;
biClrImportant := 0;
{dsBitfields: array[0..2] of DWORD;
dshSection: THandle;
dsOffset: DWORD;}
{$ifdef DebugGDKTraps}BeginGDKErrorTrap;{$endif}
case GDIBitmapType of
gbBitmap:
if GDIBitmapObject <> nil
then begin
gdk_window_get_size(GDIBitmapObject, @biWidth, @biHeight);
NumColors := 2;
biBitCount := 1;
end;
gbPixmap:
if GDIPixmapObject.Image <> nil
then begin
{$ifdef gtk1}
gdk_window_get_geometry(GDIPixmapObject.Image, nil, nil, @biWidth, @biHeight, @ImageDepth);
{$else}
gdk_drawable_get_size(GDIPixmapObject.Image, @biWidth, @biHeight);
ImageDepth := gdk_drawable_get_depth(GDIPixmapObject.Image);
{$endif}
biBitCount := ImageDepth;
end;
gbPixbuf:
if GDIPixbufObject <> nil
then begin
biWidth := gdk_pixbuf_get_width(GDIPixbufObject);
biHeight := gdk_pixbuf_get_height(GDIPixbufObject);
biBitCount := gdk_pixbuf_get_bits_per_sample(GDIPixbufObject) * gdk_pixbuf_get_n_channels(GDIPixbufObject);
end;
end;
if Visual = nil
then begin
Visual := gdk_visual_get_best_with_depth(biBitCount);
if Visual = nil
then { Depth not supported }
Visual := gdk_visual_get_system;
SystemVisual := True; { This visual should not be referenced }
if Colormap <> nil then
gdk_colormap_unref(Colormap);
ColorMap := gdk_colormap_new(Visual, GdkTrue);
end
else
biBitCount := Visual^.Depth;
{$ifdef DebugGDKTraps}EndGDKErrorTrap;{$enDIF}
if biBitCount < 16 then
NumColors := Colormap^.Size;
biSizeImage := (((biBitCount*biWidth+31) shr 5) shl 2)*biHeight;
if GetSystemMetrics(SM_CXSCREEN) >= biWidth then
biXPelsPerMeter := GetDeviceCaps(0, LOGPIXELSX)
else
biXPelsPerMeter :=
RoundToInt((single(biWidth) / GetSystemMetrics(SM_CXSCREEN)) *
GetDeviceCaps(0, LOGPIXELSX));
if GetSystemMetrics(SM_CYSCREEN) >= biHeight then
biYPelsPerMeter := GetDeviceCaps(0, LOGPIXELSY)
else
biYPelsPerMeter :=
RoundToInt((Single(biHeight) / GetSystemMetrics(SM_CYSCREEN))*
GetDeviceCaps(0, LOGPIXELSY));
bmWidth := biWidth;
bmHeight := biHeight;
bmBitsPixel := biBitCount;
//Need to retrieve actual Number of Colors if Indexed Image
if bmBitsPixel < 16
then begin
biClrUsed := NumColors;
biClrImportant := biClrUsed;
end;
end;
if BufSize >= SizeOf(BitmapSection)
then begin
PDIBSECTION(Buf)^ := BitmapSection;
Result := SizeOf(TDIBSECTION);
end
else if BufSize>0
then begin
Move(BitmapSection,Buf^,BufSize);
Result := BufSize;
end;
end;
var
GDIObject: PGDIObject absolute GDIObj;
ALogPen: PLogPen absolute Buf;
AExtLogPen: PExtLogPen absolute Buf;
i, RequiredSize: Integer;
begin
//DebugLn('trace:[TGtkWidgetSet.GetObject]');
Result := 0;
if not IsValidGDIObject(GDIObj) then Exit;
case GDIObject^.GDIType of
gdiBitmap:
Result := GetObject_Bitmap;
gdiBrush:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetObject] gdiBrush');
end;
gdiFont:
begin
if Buf = nil
then begin
Result := SizeOf(GDIObject^.LogFont);
Exit;
end;
if BufSize >= SizeOf(GDIObject^.LogFont)
then begin
PLogfont(Buf)^ := GDIObject^.LogFont;
Result:= SizeOf(TLogFont);
end
else if BufSize > 0
then begin
Move(GDIObject^.LogFont,Buf^,BufSize);
Result:=BufSize;
end;
end;
gdiPen:
begin
if GDIObject^.IsExtPen then
begin
RequiredSize := SizeOf(TExtLogPen);
if GDIObject^.GDIPenDashesCount > 1 then
RequiredSize := RequiredSize + (GDIObject^.GDIPenDashesCount - 1) * SizeOf(DWord);
if Buf = nil then
Result := RequiredSize
else
if BufSize >= RequiredSize then
begin
Result := RequiredSize;
AExtLogPen^.elpPenStyle := GDIObject^.GDIPenStyle;
AExtLogPen^.elpWidth := GDIObject^.GDIPenWidth;
AExtLogPen^.elpBrushStyle := BS_SOLID;
AExtLogPen^.elpColor := GDIObject^.GDIPenColor.ColorRef;
AExtLogPen^.elpHatch := 0;
AExtLogPen^.elpNumEntries := GDIObject^.GDIPenDashesCount;
if GDIObject^.GDIPenDashesCount > 0 then
begin
for i := 0 to GDIObject^.GDIPenDashesCount - 1 do
PDWord(@AExtLogPen^.elpStyleEntry)[i] := GDIObject^.GDIPenDashes[i];
end
else
AExtLogPen^.elpStyleEntry[0] := 0;
end;
end
else
begin
if Buf = nil then
Result := SizeOf(TLogPen)
else
if BufSize >= SizeOf(TLogPen) then
begin
Result := SizeOf(TLogPen);
ALogPen^.lopnColor := GDIObject^.GDIPenColor.ColorRef;
ALogPen^.lopnWidth := Point(GDIObject^.GDIPenWidth, 0);
ALogPen^.lopnStyle := GDIObject^.GDIPenStyle;
end;
end;
end;
gdiRegion:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetObject] gdiRegion');
end;
else
DebugLn('WARNING: [TGtkWidgetSet.GetObject] Unknown type %d', [Integer(GDIObject^.GDIType)]);
end;
end;
{------------------------------------------------------------------------------
Function: GetParent
Params: Handle:
Returns:
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetParent(Handle : HWND): HWND;
begin
if Handle <> 0 then
Result := HWnd(PGtkWidget(Handle)^.Parent)
else
Result := 0;
end;
{------------------------------------------------------------------------------
Function: GetProp
Params: Handle: Str
Returns: Pointer
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetProp(Handle : hwnd; Str : PChar): Pointer;
Begin
Result := gtk_object_get_data(pgtkobject(Handle),Str);
end;
{------------------------------------------------------------------------------
function TGtkWidgetSet.GetScrollBarSize(Handle: HWND; BarKind: Integer): integer;
Returns the current width of the scrollbar of the widget.
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetScrollBarSize(Handle: HWND; BarKind: Integer): integer;
var
Widget, ScrollWidget, BarWidget: PGtkWidget;
begin
Result:=0;
Widget:=PGtkWidget(Handle);
if GtkWidgetIsA(Widget,GTK_TYPE_SCROLLED_WINDOW) then begin
ScrollWidget:=Widget;
end else begin
ScrollWidget:=PGtkWidget(gtk_object_get_data(
PGtkObject(Widget),odnScrollArea));
end;
if ScrollWidget=nil then exit;
if BarKind=SM_CYVSCROLL then begin
BarWidget:=PGtkScrolledWindow(ScrollWidget)^.vscrollbar;
if BarWidget<>nil then
Result:=BarWidget^.Requisition.Width;
end else begin
BarWidget:=PGtkScrolledWindow(ScrollWidget)^.hscrollbar;
if BarWidget<>nil then
Result:=BarWidget^.Requisition.Height;
end;
end;
{------------------------------------------------------------------------------
function TGtkWidgetSet.GetScrollbarVisible(Handle: HWND;
SBStyle: Integer): boolean;
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetScrollbarVisible(Handle: HWND; SBStyle: Integer): boolean;
var
Widget, ScrollWidget, BarWidget: PGtkWidget;
begin
Result:=false;
if Handle=0 then exit;
Widget:=PGtkWidget(Handle);
if GtkWidgetIsA(Widget,GTK_TYPE_SCROLLED_WINDOW) then begin
ScrollWidget:=Widget;
end else begin
ScrollWidget:=PGtkWidget(gtk_object_get_data(
PGtkObject(Widget),odnScrollArea));
end;
if ScrollWidget=nil then exit;
if SBStyle=SB_VERT then begin
BarWidget:=PGtkScrolledWindow(ScrollWidget)^.vscrollbar;
end else begin
BarWidget:=PGtkScrolledWindow(ScrollWidget)^.hscrollbar;
end;
if BarWidget<>nil then
Result:=GTK_WIDGET_VISIBLE(BarWidget);
end;
{------------------------------------------------------------------------------
Function: GetScrollInfo
Params: Handle, BarFlag, ScrollInfo
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetScrollInfo(Handle: HWND; SBStyle: Integer;
var ScrollInfo: TScrollInfo): Boolean;
var
Adjustment: PGtkAdjustment;
Scroll : PGTKWidget;
IsScrollWindow: Boolean;
begin
Result := false;
if (Handle = 0) then exit;
Scroll := gtk_object_get_data(PGTKObject(Handle), odnScrollArea);
if GtkWidgetIsA(Scroll, gtk_scrolled_window_get_type)
then begin
IsScrollWindow := True;
end
else begin
Scroll := PGTKWidget(Handle);
IsScrollWindow := GtkWidgetIsA(Scroll, gtk_scrolled_window_get_type);
end;
Adjustment := nil;
case SBStyle of
SB_HORZ:
if IsScrollWindow
then begin
Adjustment := gtk_scrolled_window_get_hadjustment(
PGTKScrolledWindow(Scroll));
end
else if GtkWidgetIsA(PGtkWidget(Scroll),gtk_clist_get_type)
then begin
//clist
{TODO check is this is needed for listviews}
DebugLn('[GetScrollInfo] Possible obsolete get use of CList (Listview ?)');
Adjustment := gtk_clist_get_hadjustment(PgtkCList(Scroll));
end
// obsolete stuff
else if GtkWidgetIsA(PGtkWidget(Scroll),gtk_hscrollbar_get_type)
then begin
// this one shouldn't be possible, scrolbar messages are sent to the CTL
DebugLN('!!! direct SB_HORZ get call to scrollbar');
Adjustment := PgtkhScrollBar(Scroll)^.Scrollbar.Range.Adjustment;
end;
SB_VERT:
if GtkWidgetIsA(PGtkWidget(Scroll),gtk_scrolled_window_get_type)
then begin
Adjustment := gtk_scrolled_window_get_vadjustment(
PGTKScrolledWindow(Scroll));
end
else if GtkWidgetIsA(PGtkWidget(Scroll), gtk_clist_get_type)
then begin
//clist
//TODO: check is this is needed for listviews
DebugLn('[GetScrollInfo] Possible obsolete get use of CList (Listview ?)');
Adjustment := gtk_clist_get_vadjustment(PgtkCList(Scroll));
end
// obsolete stuff
else if GtkWidgetIsA(PGtkWidget(Scroll),gtk_vscrollbar_get_type)
then begin
// this one shouldn't be possible, scrolbar messages are sent to the CTL
DebugLN('!!! direct SB_HORZ get call to scrollbar');
Adjustment := PgtkvScrollBar(Scroll)^.Scrollbar.Range.Adjustment;
end;
SB_CTL:
if GtkWidgetIsA(PGtkWidget(Scroll),gtk_vscrollbar_get_type) then
Adjustment := PgtkvScrollBar(Scroll)^.Scrollbar.Range.Adjustment
else
if GtkWidgetIsA(PGtkWidget(Scroll),gtk_hscrollbar_get_type) then
Adjustment := PgtkhScrollBar(Scroll)^.Scrollbar.Range.Adjustment
else
if GtkWidgetIsA(PGtkWidget(Scroll), gtk_range_get_type) then
Adjustment := gtk_range_get_adjustment(PGTKRange(Scroll));
SB_BOTH:
DebugLn('[GetScrollInfo] Got SB_BOTH ???');
end;
if Adjustment = nil then Exit;
// POS
if (ScrollInfo.fMask and SIF_POS) <> 0
then begin
ScrollInfo.nPos := Round(Adjustment^.Value);
end;
// RANGE
if (ScrollInfo.fMask and SIF_RANGE) <> 0
then begin
ScrollInfo.nMin:= Round(Adjustment^.Lower);
ScrollInfo.nMax:= Round(Adjustment^.Upper);
end;
// PAGE
if (ScrollInfo.fMask and SIF_PAGE) <> 0
then begin
ScrollInfo.nPage := Round(Adjustment^.Page_Size);
end;
// TRACKPOS
if (ScrollInfo.fMask and SIF_TRACKPOS) <> 0
then begin
ScrollInfo.nTrackPos := Round(Adjustment^.Value);
end;
Result := true;
end;
{------------------------------------------------------------------------------
Function: GetStockObject
Params:
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetStockObject(Value: Integer): THandle;
begin
//DebugLn(Format('Trace:> [TGtkWidgetSet.GetStockObject] %d', [Value]));
Result := 0;
case Value of
BLACK_BRUSH: // Black brush.
Result := FStockBlackBrush;
DKGRAY_BRUSH: // Dark gray brush.
Result := FStockDKGrayBrush;
GRAY_BRUSH: // Gray brush.
Result := FStockGrayBrush;
LTGRAY_BRUSH: // Light gray brush.
Result := FStockLtGrayBrush;
NULL_BRUSH: // Null brush (equivalent to HOLLOW_BRUSH).
Result := FStockNullBrush;
WHITE_BRUSH: // White brush.
Result := FStockWhiteBrush;
BLACK_PEN: // Black pen.
Result := FStockBlackPen;
NULL_PEN: // Null pen.
Result := FStockNullPen;
WHITE_PEN: // White pen.
Result := FStockWhitePen;
(* ANSI_FIXED_FONT: // Fixed-pitch (monospace) system font.
begin
{If FStockFixedFont = 0 then
FStockFixedFont := GetStockFixedFont;
Result := FStockFixedFont;}
end;
ANSI_VAR_FONT: // Variable-pitch (proportional space) system font.
begin
end;
DEVICE_DEFAULT_FONT: // Device-dependent font.
begin
end; *)
(* OEM_FIXED_FONT: // Original equipment manufacturer (OEM) dependent fixed-pitch (monospace) font.
begin
end;
*)
DEFAULT_GUI_FONT, SYSTEM_FONT: // System font. By default, Windows uses the system font to draw menus, dialog box controls, and text. In Windows versions 3.0 and later, the system font is a proportionally spaced font; earlier versions of Windows used a monospace system font.
begin
// MG: this should only be done, when theme changed:
{If FStockSystemFont <> 0 then begin //This is a Temporary Hack!!! This
DeleteObject(FStockSystemFont); //should really only be done on
FStockSystemFont := 0; //theme change.
end;}
If FStockSystemFont = 0 then
FStockSystemFont := HFont(PtrUInt(CreateDefaultFont));
Result := FStockSystemFont;
end;
(* SYSTEM_FIXED_FONT: // Fixed-pitch (monospace) system font used in Windows versions earlier than 3.0. This stock object is provided for compatibility with earlier versions of Windows.
begin
Result := GetStockObject(ANSI_FIXED_FONT);
end;
DEFAULT_PALETTE: // Default palette. This palette consists of the static colors in the system palette.
begin
end;
*) else
//DebugLn(Format('Trace:TODO: [TGtkWidgetSet.GetStockObject] Implement value: %d', [Value]));
end;
//DebugLn(Format('Trace:< [TGtkWidgetSet.GetStockObject] %d --> 0x%x', [Value, Result]));
end;
{------------------------------------------------------------------------------
Function: GetSysColor
Params: index to the syscolors array
Returns: RGB value
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetSysColor(nIndex: Integer): DWORD;
begin
if (nIndex < 0) or (nIndex > MAX_SYS_COLORS)
then begin
Result := 0;
DumpStack;
DebugLn(Format('ERROR: [TGtkWidgetSet.GetSysColor] Bad Value: %d. Valid Range between 0 and %d', [nIndex, MAX_SYS_COLORS]));
end
else
Result := SysColorMap[nIndex];
end;
function TGTKWidgetSet.GetSysColorBrush(nIndex: Integer): HBrush;
begin
if (nIndex < 0) or (nIndex > MAX_SYS_COLORS)
then begin
Result := 0;
DumpStack;
DebugLn(Format('ERROR: [TGtkWidgetSet.GetSysColorBrush] Bad Value: %d. Valid Range between 0 and %d', [nIndex, MAX_SYS_COLORS]));
end
else
Result := FSysColorBrushes[nIndex];
end;
{------------------------------------------------------------------------------
Function: GetSystemMetrics
Params:
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetSystemMetrics(nIndex: Integer): Integer;
var
P: Pointer;
{$ifdef HasX}
ax,ay,ah,aw: gint;
{$endif}
auw, auh: guint;
{$ifdef GTK2}
screen: PGdkScreen;
ARect: TGdkRectangle;
AValue: TGValue;
{$else}
{$ifdef HasX}
XDisplay: PDisplay;
XScreen: PScreen;
XWindow: TWindow;
{$endif}
{$endif}
begin
//DebugLn(Format('Trace:> [TGtkWidgetSet.GetSystemMetrics] %d', [nIndex]));
Result := 0;
case nIndex of
SM_ARRANGE:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_ARRANGE ');
end;
SM_CLEANBOOT:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CLEANBOOT ');
end;
SM_CMOUSEBUTTONS:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CMOUSEBUTTONS ');
end;
SM_CXBORDER:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CXBORDER ');
end;
SM_CYBORDER:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CYBORDER ');
end;
SM_CXCURSOR,
SM_CYCURSOR:
begin
{$IFDEF GTK2}
// Width and height of a cursor, in pixels. For win32 system cannot create cursors of other sizes.
// For gtk this should be maximal cursor sizes
gdk_display_get_maximal_cursor_size(gdk_display_get_default, @auw, @auh);
{$ELSE}
{$IFDEF HasX}
// same code used in gtk2 library
XDisplay := gdk_display;
XScreen := XDefaultScreenOfDisplay(XDisplay);
XWindow := XRootWindowOfScreen(XScreen);
XQueryBestCursor(XDisplay, XWindow, 128, 128, @auw, @auh);
{$ELSE}
Result := 32; // Default windows size
{$ENDIF}
{$ENDIF}
if nIndex = SM_CXCURSOR
then Result := auw // return width
else Result := auh; // return height
end;
SM_CXDOUBLECLK:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CXDOUBLECLK ');
end;
SM_CYDOUBLECLK:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CYDOUBLECLK ');
end;
SM_CXDRAG:
begin
Result := 2;
end;
SM_CYDRAG:
begin
Result := 2;
end;
SM_CXEDGE:
begin
Result := 2;
end;
SM_CYEDGE:
begin
Result := 2;
end;
SM_CXFIXEDFRAME:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CXFIXEDFRAME ');
end;
SM_CYFIXEDFRAME:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CYFIXEDFRAME ');
end;
SM_CXHSCROLL:
begin
P := GetStyleWidget(lgsVerticalScrollbar);
if P <> nil then
Result := GTK_Widget(P)^.requisition.Width;
end;
SM_CYHSCROLL:
begin
P := GetStyleWidget(lgsHorizontalScrollbar);
if P <> nil then
Result := GTK_Widget(P)^.requisition.Height;
end;
SM_CXHTHUMB,
SM_CYVTHUMB:
begin
P := GetStyleWidget(lgsHorizontalScrollbar);
if P <> nil then
begin
{$ifdef gtk1}
_gtk_range_get_props(P, nil, nil, @Result, nil);
{$else}
FillChar(AValue, SizeOf(AValue), 0);
g_value_init(@AValue, G_TYPE_INT);
gtk_widget_style_get_property(P, 'slider-width', @AValue);
Result := AValue.data[0].v_int;
{$endif}
end;
end;
SM_CXICON,
SM_CYICON:
Result := 32;
SM_CXICONSPACING:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CXICONSPACING ');
end;
SM_CYICONSPACING:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CYICONSPACING ');
end;
SM_CXMAXIMIZED:
begin
{$IFDEF HasX}
if XGetWorkarea(ax,ay,aw,ah)>=0 then
Result := aw
else
Result := getSystemMetrics(SM_CXSCREEN);
{$ENDIF}
end;
SM_CYMAXIMIZED:
begin
{$IFDEF HasX}
if XGetWorkarea(ax,ay,aw,ah)>=0 then
Result := ah
else
Result := getSystemMetrics(SM_CYSCREEN);
{$ENDIF}
end;
SM_CXMAXTRACK:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CXMAXTRACK ');
end;
SM_CYMAXTRACK:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CYMAXTRACK ');
end;
SM_CXMENUCHECK:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CXMENUCHECK ');
end;
SM_CYMENUCHECK:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CYMENUCHECK ');
end;
SM_CXMENUSIZE:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CXMENUSIZE ');
end;
SM_CYMENUSIZE:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CYMENUSIZE ');
end;
SM_CXMIN:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CXMIN ');
end;
SM_CYMIN:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CYMIN ');
end;
SM_CXMINIMIZED:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CXMINIMIZED ');
end;
SM_CYMINIMIZED:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CYMINIMIZED ');
end;
SM_CXMINSPACING:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CXMINSPACING ');
end;
SM_CYMINSPACING:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CYMINSPACING ');
end;
SM_CXMINTRACK:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CXMINTRACK ');
end;
SM_CYMINTRACK:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CYMINTRACK ');
end;
SM_CXFULLSCREEN,
SM_CXSCREEN:
begin
{ Partial fix for multi monitor systems - force use of first one }
{$ifdef UseXinerama}
if GetFirstScreen then
result := FirstScreen.x
else
{$endif}
result := gdk_Screen_Width;
end;
SM_CXVIRTUALSCREEN:
begin
Result := gdk_Screen_Width;
end;
SM_CYFULLSCREEN,
SM_CYSCREEN:
begin
{$ifdef UseXinerama}
if GetFirstScreen then
result := FirstScreen.y
else
{$endif}
result := gdk_Screen_Height;
end;
SM_CYVIRTUALSCREEN:
begin
result := gdk_Screen_Height;
end;
SM_CXSIZE:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CXSIZE ');
end;
SM_CYSIZE:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CYSIZE ');
end;
SM_CXSIZEFRAME,
SM_CYSIZEFRAME:
begin
Result := 4;
end;
SM_CXSMICON,
SM_CYSMICON:
Result := 16;
SM_CXSMSIZE:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CXSMSIZE ');
end;
SM_CYSMSIZE:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CYSMSIZE ');
end;
SM_CXVSCROLL:
begin
P := GetStyleWidget(lgsVerticalScrollbar);
if P <> nil then
Result := GTK_Widget(P)^.requisition.Width;
end;
SM_CYVSCROLL:
begin
P := GetStyleWidget(lgsHorizontalScrollbar);
if P <> nil then
Result := GTK_Widget(P)^.requisition.Height;
end;
SM_CYCAPTION:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CYCAPTION ');
end;
SM_CYKANJIWINDOW:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CYKANJIWINDOW ');
end;
SM_CYMENU:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CYMENU ');
end;
SM_CYSMCAPTION:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_CYSMCAPTION ');
end;
SM_DBCSENABLED:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_DBCSENABLED ');
end;
SM_DEBUG:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_DEBUG ');
end;
SM_MENUDROPALIGNMENT:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_MENUDROPALIGNMENT');
end;
SM_MIDEASTENABLED:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_MIDEASTENABLED ');
end;
SM_MOUSEPRESENT:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_MOUSEPRESENT ');
end;
SM_MOUSEWHEELPRESENT:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_MOUSEWHEELPRESENT');
end;
SM_NETWORK:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_NETWORK ');
end;
SM_PENWINDOWS:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_PENWINDOWS ');
end;
SM_SECURE:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_SECURE ');
end;
SM_SHOWSOUNDS:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_SHOWSOUNDS ');
end;
SM_SLOWMACHINE:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_SLOWMACHINE ');
end;
SM_SWAPBUTTON:
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.GetSystemMetrics] --> SM_SWAPBUTTON ');
end;
SM_SWSCROLLBARSPACING:
begin
P := GetStyleWidget(lgsScrolledWindow);
if P <> nil then begin
{$IFDEF GTK2}
result := GTK_SCROLLED_WINDOW_CLASS(gtk_widget_get_class(P))^.scrollbar_spacing;
if result<0 then
gtk_widget_style_get(P, 'scrollbar-spacing', @result, nil);
{$ELSE}
result := PGtkScrolledWindowClass(PGtkTypeObject(P)^.klass)^.scrollbar_spacing;
if result<0 then
result := 3;
{$ENDIF}
end;
end;
end;
//DebugLn(Format('Trace:< [TGtkWidgetSet.GetSystemMetrics] %d --> 0x%x (%d)', [nIndex, Result, Result]));
end;
{------------------------------------------------------------------------------
Function: GetTextColor
Params: DC
Returns: TColorRef
Gets the Font Color currently assigned to the Device Context
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetTextColor(DC: HDC) : TColorRef;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
Result := 0;
if IsValidDC(DC) then
with TGtkDeviceContext(DC) do
begin
Result := CurrentTextColor.ColorRef;
end;
end;
{------------------------------------------------------------------------------
Function: GetTextExtentPoint
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetTextExtentPoint(DC: HDC; Str: PChar; Count: Integer;
var Size: TSize): Boolean;
{$IfDef GTK2}
begin
DebugLn('TGtkWidgetSet.GetTextExtentPoint ToDo');
Result:=false;
end;
{$Else}
var
DevCtx: TGtkDeviceContext absolute DC;
lbearing, rbearing, width, ascent,descent: LongInt;
UseFont : PGDKFont;
IsDBCSFont: Boolean;
NewCount: Integer;
begin
Result := IsValidDC(DC);
if Result
then with TGtkDeviceContext(DC) do
begin
UseFont:=GetGtkFont(TGtkDeviceContext(DC));
descent:=0;
UpdateDCTextMetric(TGtkDeviceContext(DC));
IsDBCSFont:=TGtkDeviceContext(DC).DCTextMetric.IsDoubleByteChar;
if IsDBCSFont then begin
NewCount:=Count*2;
if FExtUTF8OutCacheSize<NewCount then begin
ReAllocMem(FExtUTF8OutCache,NewCount);
FExtUTF8OutCacheSize:=NewCount;
end;
NewCount:=UTF8ToDoubleByte(Str,Count,FExtUTF8OutCache)*2;
gdk_text_extents(UseFont, FExtUTF8OutCache, NewCount,
@lbearing, @rBearing, @width, @ascent, @descent);
end else begin
gdk_text_extents(UseFont, Str, Count,
@lbearing, @rBearing, @width, @ascent, @descent);
end;
Size.cX := Width;
// I THINK this is accurate...
Size.cY :={$IFDEF Win32}
GDK_String_Height(UseFont, Str)
{$ELSE}
ascent+descent;
{$ENDIF}
//debugln('TGtkWidgetSet.GetTextExtentPoint END Str="'+DbgStr(Str)+'" Size=',dbgs(Size.cX),'x',dbgs(Size.cY),' ascent=',dbgs(ascent),' descent=',dbgs(descent),' tmDescent=',dbgs(TGtkDeviceContext(DC).DCTextMetric.TextMetric.tmDescent));
if DevCtx.HasTransf then
begin
DevCtx.InvTransfExtent(Size.cx, Size.cy);
Size.cx := Abs(Size.cx);
Size.cy := Abs(Size.cy);
end;
end;
//DebugLn('trace:< [TGtkWidgetSet.GetTextExtentPoint]');
end;
{$EndIf}
{------------------------------------------------------------------------------
Function: GetTextMetrics
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetTextMetrics(DC: HDC; var TM: TTextMetric): Boolean;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
//DebugLn(Format('Trace:> TODO FINISH[TGtkWidgetSet.GetTextMetrics] DC: 0x%x', [DC]));
Result := IsValidDC(DC);
if Result then
begin
UpdateDCTextMetric(DevCtx);
TM := DevCtx.DCTextMetric.TextMetric;
end;
//DebugLn(Format('Trace:< TODO FINISH[TGtkWidgetSet.GetTextMetrics] DC: 0x%x', [DC]));
end;
function TGtkWidgetSet.GetViewPortExtEx(DC: HDC; Size: PSize): Integer;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
if IsValidDC(DC) and (Size <> nil) then
begin
Size^.cx := DevCtx.ViewPortExt.x;
Size^.cy := DevCtx.ViewPortExt.y;
Result := Integer(True);
end else
Result := Integer(False);
end;
function TGtkWidgetSet.GetViewPortOrgEx(DC: HDC; P: PPoint): Integer;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
if IsValidDC(DC) and (P <> nil) then
begin
P^.x := DevCtx.ViewPortOrg.x;
P^.y := DevCtx.ViewPortOrg.y;
Result := Integer(True);
end else
Result := Integer(False);
end;
function TGtkWidgetSet.GetWindowExtEx(DC: HDC; Size: PSize): Integer;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
if IsValidDC(DC) and (Size <> nil) then
begin
Size^.cx := DevCtx.WindowExt.x;
Size^.cy := DevCtx.WindowExt.y;
Result := Integer(True);
end else
Result := Integer(False);
end;
{------------------------------------------------------------------------------
Function: GetWindowLong
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetWindowLong(Handle: HWND; int: Integer): PtrInt;
function GetObjectData(Name: PChar): PtrInt;
begin
Result := PtrInt(PtrUInt(gtk_object_get_data(pgtkobject(Handle),Name)));
end;
var
WidgetInfo: PWidgetInfo;
begin
//TODO:Started but not finished
//DebugLn(Format('Trace:> [TGtkWidgetSet.GETWINDOWLONG] HWND: 0x%x, int: 0x%x (%d)', [Handle, int, int]));
case int of
GWL_WNDPROC :
begin
WidgetInfo := GetWidgetInfo(Pointer(Handle));
if WidgetInfo <> nil then
Result := WidgetInfo^.WndProc
else
Result := 0;
end;
GWL_HINSTANCE :
begin
Result := GetObjectData('HINSTANCE');
end;
GWL_HWNDPARENT :
begin
Result := GetObjectData('HWNDPARENT');
end;
{ GWL_WNDPROC :
begin
Data := GetLCLObject(Pointer(Handle));
if Data is TControl
then Result := PtrInt(@(TControl(Data).WindowProc));
// TODO fix this, a method pointer (2 pointers) can not be casted to a longint
end;
}
{ GWL_HWNDPARENT :
begin
Data := GetLCLObject(Pointer(Handle));
if (Data is TWinControl)
then Result := PtrInt(TWincontrol(Data).Handle)
else Result := 0;
end;
}
GWL_STYLE :
begin
WidgetInfo := GetWidgetInfo(Pointer(Handle));
if WidgetInfo <> nil then
Result := WidgetInfo^.Style
else
Result := 0;
end;
GWL_EXSTYLE :
begin
WidgetInfo := GetWidgetInfo(Pointer(Handle));
if WidgetInfo <> nil then
Result := WidgetInfo^.ExStyle
else
Result := 0;
end;
GWL_USERDATA :
begin
Result := GetObjectData('Userdata');
end;
GWL_ID :
begin
Result := GetObjectData('ID');
end;
else Result := 0;
end; //case
//DebugLn(Format('Trace:< [TGtkWidgetSet.GETWINDOWLONG] HWND: 0x%x, int: 0x%x (%d) --> 0x%x (%d)', [Handle, int, int, Result, Result]));
end;
{------------------------------------------------------------------------------
Function: GetWindowOrgEx
Params: none
Returns: Nothing
Returns the current offset of the DC.
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetWindowOrgEx(dc : hdc; P : PPoint): Integer;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
if P = nil then Exit(0);
P^ := Point(0,0);
if not IsValidDC(DC) then exit(0);
P^ := DevCtx.Offset;
Result:=1;
end;
{------------------------------------------------------------------------------
Function: GetWindowRect
Params: none
Returns: 0
After the call, ARect will be the control area in screen coordinates.
That means, Left and Top will be the screen coordinate of the TopLeft pixel
of the Handle object and Right and Bottom will be the screen coordinate of
the BottomRight pixel.
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetWindowRect(Handle: hwnd; var ARect: TRect): Integer;
var
Widget: PGTKWidget;
begin
//DebugLn('GetWindowRect');
Result := 0; //default
if Handle <> 0 then
begin
Widget := PGtkWidget(Handle);
ARect.TopLeft := GetWidgetOrigin(Widget);
ARect.BottomRight := Point(ARect.Left + Widget^.allocation.width,
ARect.Top + Widget^.allocation.height);
end;
end;
{------------------------------------------------------------------------------
Function: GetWindowRelativePosition
Params: Handle : hwnd;
Returns: true on success
Returns the Left, Top, relative to the client origin of its parent
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetWindowRelativePosition(Handle : hwnd;
var Left, Top: integer): boolean;
var
aWidget: PGtkWidget;
begin
aWidget := PGtkWidget(Handle);
if GtkWidgetIsA(aWidget, GTK_TYPE_WIDGET) then
begin
Result := true;
GetWidgetRelativePosition(aWidget, Left, Top);
end else
Result := false;
end;
{------------------------------------------------------------------------------
Function: GetWindowSize
Params: Handle : hwnd;
Returns: true on success
Returns the current widget Width and Height
------------------------------------------------------------------------------}
function TGtkWidgetSet.GetWindowSize(Handle : hwnd;
var Width, Height: integer): boolean;
begin
if GtkWidgetIsA(PGtkWidget(Handle),GTK_TYPE_WIDGET) then begin
Result:=true;
Width:=Max(0,PGtkWidget(Handle)^.Allocation.Width);
Height:=Max(0,PGtkWidget(Handle)^.Allocation.Height);
//DebugLn(['TGtkWidgetSet.GetWindowSize ',DbgSName(GetLCLOwnerObject(Handle)),' Allocation=',Width,'x',Height]);
end else
Result:=false;
end;
{------------------------------------------------------------------------------
Function: GradientFill
Params: DC - DeviceContext to perform on
Vertices - array of Points W/Color & Alpha
NumVertices - Number of Vertices
Meshes - array of Triangle or Rectangle Meshes,
each mesh representing one Gradient Fill
NumMeshes - Number of Meshes
Mode - Gradient Type, either Triangle,
Vertical Rect, Horizontal Rect
Returns: true on success
Performs multiple Gradient Fills, either a Three way Triangle Gradient,
or a two way Rectangle Gradient, each Vertex point also supports optional
Alpha/Transparency for more advanced Gradients.
------------------------------------------------------------------------------}
function TGtkWidgetSet.GradientFill(DC: HDC; Vertices: PTriVertex;
NumVertices : Longint; Meshes: Pointer; NumMeshes : Longint; Mode : Longint
): Boolean;
var
DevCtx: TGtkDeviceContext absolute DC;
function DoFillTriangle : Boolean;
begin
Result := (Mode and GRADIENT_FILL_TRIANGLE) = GRADIENT_FILL_TRIANGLE;
end;
function DoFillVRect : Boolean;
begin
Result := (Mode and GRADIENT_FILL_RECT_V) = GRADIENT_FILL_RECT_V;
end;
procedure GetGradientBrush(BeginColor, EndColor : TColorRef; Position,
TotalSteps : Longint; var GradientBrush : hBrush);
var
R1, G1, B1 : Integer;
R2, G2, B2 : Integer;
NewBrush : TLogBrush;
begin
GetRGBIntValues(BeginColor,R1,G1,B1);
GetRGBIntValues(EndColor,R2,G2,B2);
R1 := R1 + (Position*(R2 - R1) div TotalSteps);
G1 := G1 + (Position*(G2 - G1) div TotalSteps);
B1 := B1 + (Position*(B2 - B1) div TotalSteps);
with NewBrush do
begin
lbStyle := BS_SOLID;
lbColor := RGB(R1,G1,B1);
end;
If GradientBrush <> 0 then
LCLIntf.DeleteObject(GradientBrush);
GradientBrush := LCLIntf.CreateBrushIndirect(NewBrush);
end;
function FillTriMesh(Mesh : tagGradientTriangle) : Boolean;
{var
V1, V2, V3 : tagTRIVERTEX;
C1, C2, C3 : TColorRef;
begin
With Mesh do begin
Result := (Vertex1 < NumVertices) and (Vertex2 >= 0) and
(Vertex2 < NumVertices) and (Vertex2 >= 0) and
(Vertex3 < NumVertices) and (Vertex3 >= 0);
If (Vertex1 = Vertex2) or (Vertex1 = Vertex3) or
(Vertex2 = Vertex3) or not Result
then
exit;
V1 := Vertices[Vertex1];
V2 := Vertices[Vertex2];
V3 := Vertices[Vertex3];
//Check to make sure they are in reasonable positions..
//then what??
end;}
begin
Result := False;
end;
function FillRectMesh(Mesh : tagGradientRect) : Boolean;
var
TL, BR: tagTRIVERTEX;
StartColor, EndColor: TColorRef;
I, Swap: Longint;
SwapColors: Boolean;
UseBrush: hBrush;
Steps, MaxSteps: Int64;
begin
with Mesh do
begin
Result := (UpperLeft < NumVertices) and (UpperLeft >= 0) and
(LowerRight < NumVertices) and (LowerRight >= 0);
if (LowerRight = UpperLeft) or not Result then
exit;
TL := Vertices[UpperLeft];
BR := Vertices[LowerRight];
SwapColors := (BR.Y < TL.Y) and (BR.X < TL.X);
if BR.X < TL.X then
begin
Swap := BR.X;
BR.X := TL.X;
TL.X := Swap;
end;
if BR.Y < TL.Y then
begin
Swap := BR.Y;
BR.Y := TL.Y;
TL.Y := Swap;
end;
StartColor := RGB(TL.Red shr 8, TL.Green shr 8, TL.Blue shr 8);
EndColor := RGB(BR.Red shr 8, BR.Green shr 8, BR.Blue shr 8);
if SwapColors then
begin
Swap := StartColor;
StartColor := EndColor;
EndColor := Swap;
end;
UseBrush := 0;
MaxSteps := GetDeviceCaps(DC, BITSPIXEL);
if MaxSteps >= 32 then
MaxSteps := $FFFFFFFF
else
if MaxSteps >= 4 then
MaxSteps := 1 shl MaxSteps
else
MaxSteps := 256;
if DoFillVRect then
begin
Steps := Min(BR.Y - TL.Y, MaxSteps);
for I := 0 to Steps - 1 do
begin
GetGradientBrush(StartColor, EndColor, I, Steps, UseBrush);
LCLIntf.FillRect(DC, Rect(TL.X, TL.Y + I, BR.X, TL.Y + I + 1),
UseBrush)
end
end
else begin
Steps := Min(BR.X - TL.X, MaxSteps);
for I := 0 to Steps - 1 do
begin
GetGradientBrush(StartColor, EndColor, I, Steps, UseBrush);
LCLIntf.FillRect(DC, Rect(TL.X + I, TL.Y, TL.X + I + 1, BR.Y),
UseBrush);
end;
end;
If UseBrush <> 0 then
LCLIntf.DeleteObject(UseBrush);
end;
end;
const
MeshSize: Array[Boolean] of Integer = (
SizeOf(tagGradientRect), SizeOf(tagGradientTriangle));
var
I : Integer;
begin
//Currently Alpha blending is ignored... Ideas anyone?
Result := (Meshes <> nil) and (NumMeshes >= 1) and (NumVertices >= 2)
and (Vertices <> nil);
if Result and DoFillTriangle then
Result := NumVertices >= 3;
if Result then
begin
Result := False;
//Sanity Checks For Vertices Size vs. Count
if MemSize(Vertices) < PtrInt(SizeOf(tagTRIVERTEX)*NumVertices) then
exit;
//Sanity Checks For Meshes Size vs. Count
if MemSize(Meshes) < PtrInt(MeshSize[DoFillTriangle]*NumMeshes) then
exit;
for I := 0 to NumMeshes - 1 do
begin
if DoFillTriangle then
begin
If not FillTriMesh(PGradientTriangle(Meshes)[I]) then
exit;
end
else
begin
if not FillRectMesh(PGradientRect(Meshes)[I]) then
exit;
end;
end;
Result := True;
end;
end;
{------------------------------------------------------------------------------
Function: HideCaret
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.HideCaret(hWnd: HWND): Boolean;
var
GTKObject: PGTKObject;
WasVisible: boolean;
begin
//DebugLn('[TGtkWidgetSet.HideCaret] A');
//DebugLn(Format('Trace: [TGtkWidgetSet.HideCaret] HWND: 0x%x', [hWnd]));
//TODO: [TGtkWidgetSet.HideCaret] Finish (in gtkwinapi.inc)
GTKObject := PGTKObject(HWND);
Result := GTKObject <> nil;
if Result
then begin
if gtk_type_is_a(gtk_object_type(GTKObject), GTKAPIWidget_GetType)
then begin
GTKAPIWidget_HideCaret(PGTKAPIWidget(GTKObject),WasVisible);
end
// else if // TODO: other widgettypes
else begin
Result := False;
end;
end
else DebugLn('WARNING: [TGtkWidgetSet.HideCaret] Got null HWND');
end;
{------------------------------------------------------------------------------
Function: IntersectClipRect
Params: dc: hdc; Left, Top, Right, Bottom: Integer
Returns: Integer
Shrinks the clipping region in the device context dc to a region of all
intersecting points between the boundary defined by Left, Top, Right,
Bottom , and the Current clipping region.
The result can be one of the following constants
Error
NullRegion
SimpleRegion
ComplexRegion
------------------------------------------------------------------------------}
function TGtkWidgetSet.IntersectClipRect(dc: hdc;
Left, Top, Right, Bottom: Integer): Integer;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
if not IsValidDC(DC) then Exit;
if DevCtx.HasTransf then
begin
DevCtx.TransfRect(Left, Top, Right, Bottom);
DevCtx.TransfNormalize(Left, Right);
DevCtx.TransfNormalize(Top, Bottom);
end;
Result := inherited IntersectClipRect(DC, Left, Top, Right, Bottom);
end;
{------------------------------------------------------------------------------
Function: InvalidateRect
Params: aHandle:
Rect:
bErase:
Returns:
------------------------------------------------------------------------------}
function TGtkWidgetSet.InvalidateRect(aHandle : HWND; Rect : pRect;
bErase : Boolean) : Boolean;
var
gdkRect : TGDKRectangle;
Widget, PaintWidget: PGtkWidget;
LCLObject: TObject;
r: TRect;
List: PGList;
i: Integer;
Pt: TPoint;
Adjustment: PGtkAdjustment;
Scrolled: PGtkScrolledWindow;
begin
// DebugLn(format('Rect = %d,%d,%d,%d',[rect^.left,rect^.top,rect^.Right,rect^.Bottom]));
Widget:=PGtkWidget(aHandle);
LCLObject:=GetLCLObject(Widget);
if (LCLObject<>nil) then begin
if (LCLObject=CurrentSentPaintMessageTarget) then begin
DebugLn('NOTE: TGtkWidgetSet.InvalidateRect during paint message: ',
LCLObject.ClassName);
//DumpStack;
//RaiseGDBException('Double paint');
end;
{$IFDEF VerboseDsgnPaintMsg}
if (LCLObject is TComponent)
and (csDesigning in TComponent(LCLObject).ComponentState) then begin
write('TGtkWidgetSet.InvalidateRect A ');
write(TComponent(LCLObject).Name,':');
write(LCLObject.ClassName);
with Rect^ do
write(' Rect=',Left,',',Top,',',Right,',',Bottom);
DebugLn(' Erase=',bErase);
end;
{$ENDIF}
end;
Result := True;
PaintWidget:=GetFixedWidget(Widget);
if PaintWidget=nil then PaintWidget:=Widget;
if Rect = nil then begin
Rect := @r;
Rect^.Left := 0;//PaintWidget^.Allocation.X;
Rect^.Top := 0;//PaintWidget^.Allocation.Y;
Rect^.Right := PaintWidget^.Allocation.Width;
Rect^.Bottom := PaintWidget^.Allocation.Height;
end;
gdkRect.X := Rect^.Left;
gdkRect.Y := Rect^.Top;
gdkRect.Width := (Rect^.Right - Rect^.Left);
gdkRect.Height := (Rect^.Bottom - Rect^.Top);
if LCLObject is TScrollingWinControl then
begin
List := gtk_container_children(PGtkContainer(Widget));
if (g_list_length(List) > 0) and
GTK_IS_SCROLLED_WINDOW(g_list_nth_data(List, 0)) then
begin
Scrolled := PGtkScrolledWindow(g_list_nth_data(List, 0));
Pt := Point(0, 0);
Adjustment := gtk_scrolled_window_get_vadjustment(Scrolled);
if Adjustment <> nil then
Pt.Y := Round(Adjustment^.value);
Adjustment := gtk_scrolled_window_get_hadjustment(Scrolled);
if Adjustment <> nil then
Pt.X := Round(Adjustment^.value);
dec(gdkRect.X, Pt.X);
dec(gdkRect.Y, Pt.Y);
end;
g_list_free(List);
end;
if bErase then
gtk_widget_queue_clear_area(PaintWidget,
gdkRect.X,gdkRect.Y,gdkRect.Width,gdkRect.Height);
gtk_widget_queue_draw_area(PaintWidget,
gdkRect.X,gdkRect.Y,gdkRect.Width,gdkRect.Height);
{$IfNDef GTK1}
//DebugLn(['TGtkWidgetSet.InvalidateRect ',GetWidgetDebugReport(Widget),' IsAPI=',GtkWidgetIsA(PGTKWidget(Widget),GTKAPIWidget_GetType)]);
if GtkWidgetIsA(PGTKWidget(Widget),GTKAPIWidget_GetType) then
GTKAPIWidget_InvalidateCaret(PGTKAPIWidget(Widget));
{$EndIf}
end;
function TGTKWidgetSet.IsIconic(handle: HWND): boolean;
var
GtkWindow: PGtkWindow absolute handle;
begin
Result := False;
if GtkWindow = nil then
Exit;
{$ifdef gtk1}
Result := GDK_WINDOW_GET_MINIMIZED(PGdkWindowPrivate(PgtkWidget(GtkWindow)^.Window));
{$else}
Result := (PGtkWidget(GtkWindow)^.Window<>nil)
and (gdk_window_get_state(PGtkWidget(GtkWindow)^.Window)
and GDK_WINDOW_STATE_ICONIFIED <> 0);
{$endif}
end;
function TGTKWidgetSet.IsWindow(handle: HWND): boolean;
begin
if Handle = 0 then
Exit(False);
Result := GtkWidgetIsA(PGtkWidget(Handle), GTK_TYPE_WIDGET);
end;
{------------------------------------------------------------------------------
function TGtkWidgetSet.IsWindowEnabled(handle: HWND): boolean;
------------------------------------------------------------------------------}
function TGtkWidgetSet.IsWindowEnabled(handle: HWND): boolean;
var
LCLObject: TObject;
Widget: PGtkWidget;
AForm: TCustomForm;
//i: Integer;
begin
Widget:=PGtkWidget(handle);
Result:=(Widget<>nil) and GTK_WIDGET_SENSITIVE(Widget)
and GTK_WIDGET_PARENT_SENSITIVE(Widget);
LCLObject:=GetLCLObject(PGtkWidget(Handle));
//debugln('TGtkWidgetSet.IsWindowEnabled A ',DbgSName(LCLObject),' Result=',dbgs(Result),
// ' SENSITIVE=',dbgs(GTK_WIDGET_SENSITIVE(Widget)),
// ' PARENT_SENSITIVE=',dbgs(GTK_WIDGET_PARENT_SENSITIVE(Widget)),
// ' TOPLEVEL=',dbgs(GTK_WIDGET_TOPLEVEL(Widget)),
// '');
if Result and GtkWidgetIsA(Widget,GTK_TYPE_WINDOW) then begin
LCLObject:=GetLCLObject(Widget);
if (LCLObject is TCustomForm) then begin
AForm:=TCustomForm(LCLObject);
if not Screen.CustomFormBelongsToActiveGroup(AForm) then
Result:=false;
//debugln('TGtkWidgetSet.IsWindowEnabled B ',dbgs(Screen.CustomFormBelongsToActiveGroup(AForm)));
//for i:=0 to Screen.CustomFormCount-1 do begin
// debugln(' ',dbgs(i),' ',DbgSName(Screen.CustomFormsZOrdered[i]));
//end;
end;
end;
end;
{------------------------------------------------------------------------------
function TGtkWidgetSet.IsWindowVisible(handle: HWND): boolean;
------------------------------------------------------------------------------}
function TGtkWidgetSet.IsWindowVisible(handle: HWND): boolean;
begin
Result:=(handle<>0) and GTK_WIDGET_VISIBLE(PGtkWidget(handle));
end;
function TGTKWidgetSet.IsZoomed(handle: HWND): boolean;
var
GtkWindow: PGtkWindow absolute handle;
begin
Result := False;
if GtkWindow = nil then
Exit;
{$ifdef gtk1}
Result := GDK_WINDOW_GET_MAXIMIZED(PGdkWindowPrivate(PgtkWidget(GtkWindow)^.Window));
{$else}
Result := gdk_window_get_state(PGtkWidget(GtkWindow)^.Window) and GDK_WINDOW_STATE_MAXIMIZED <> 0;
{$endif}
end;
{------------------------------------------------------------------------------
Function: LineTo
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.LineTo(DC: HDC; X, Y: Integer): Boolean;
var
DevCtx: TGtkDeviceContext absolute DC;
DCOrigin: TPoint;
FromX: Integer;
FromY: Integer;
ToX: Integer;
ToY: Integer;
begin
//DebugLn(Format('trace:> [TGtkWidgetSet.LineTo] DC:0x%x, X:%d, Y:%d', [DC, X, Y]));
if not IsValidDC(DC) then Exit(False);
DevCtx.SelectPenProps;
if not (dcfPenSelected in DevCtx.Flags) then Exit(False);
if DevCtx.IsNullPen then Exit(True);
if DevCtx.HasTransf then
DevCtx.TransfPoint(X, Y);
DCOrigin := DevCtx.Offset;
FromX:=DevCtx.PenPos.X+DCOrigin.X;
FromY:=DevCtx.PenPos.Y+DCOrigin.Y;
ToX:=X+DCOrigin.X;
ToY:=Y+DCOrigin.Y;
{$IFDEF DebugGDK}BeginGDKErrorTrap;{$ENDIF}
gdk_draw_line(DevCtx.Drawable, DevCtx.GC, FromX, FromY, ToX, ToY);
{$IFDEF DebugGDK}EndGDKErrorTrap;{$ENDIF}
DevCtx.PenPos:= Point(X, Y);
Result := True;
//DebugLn(Format('trace:< [TGtkWidgetSet.LineTo] DC:0x%x, X:%d, Y:%d', [DC, X, Y]));
end;
function TGTKWidgetSet.LPtoDP(DC: HDC; var Points; Count: Integer): BOOL;
var
DevCtx: TGtkDeviceContext absolute DC;
P: PPoint;
begin
Result := False;
if not IsValidDC(DC) then Exit(False);
if not DevCtx.HasTransf then Exit(True);
P := @Points;
while Count > 0 do
begin
Dec(Count);
DevCtx.TransfPoint(P^.X, P^.Y);
Inc(P);
end;
Result := True;
end;
{------------------------------------------------------------------------------
Function: MessageBox
Params: hWnd: The handle of parent window
Returns: 0 if not successful (out of memory), otherwise one of the defined value :
IDABORT, IDCANCEL, IDIGNORE, IDNO, IDOK, IDRETRY, IDYES
The MessageBox function displays a modal dialog, with text and caption defined,
and includes buttons.
------------------------------------------------------------------------------}
function MessageButtonClicked(Widget : PGtkWidget; data: gPointer) : GBoolean; cdecl;
begin
//DebugLn('[MessageButtonClicked] ',dbgs(data),' ',dbgs(gtk_object_get_data(PGtkObject(Widget), 'modal_result')));
if PInteger(data)^ = 0 then
PInteger(data)^:=PtrUInt(gtk_object_get_data(PGtkObject(Widget), 'modal_result'));
Result:=false;
end;
function MessageBoxClosed(Widget : PGtkWidget; Event : PGdkEvent;
data: gPointer) : GBoolean; cdecl;
var ModalResult : PtrUInt;
begin
{ We were requested by window manager to close }
if PInteger(data)^ = 0 then begin
ModalResult:= PtrUInt(gtk_object_get_data(PGtkObject(Widget), 'modal_result'));
{ Don't allow to close if we don't have a default return value }
Result:= (ModalResult = 0);
if not Result then PInteger(data)^:= ModalResult
else DebugLn('Do not close !!!');
end else Result:= false;
end;
function TGtkWidgetSet.MessageBox(hWnd: HWND; lpText, lpCaption: PChar;
uType : Cardinal): integer;
var Dialog, ALabel : PGtkWidget;
ButtonCount, DefButton, ADialogResult : Integer;
DialogType : Cardinal;
procedure CreateButton(const ALabel : PChar; const RetValue : integer);
var AButton : PGtkWidget;
begin
AButton:= gtk_button_new_with_label(ALabel);
Inc(ButtonCount);
if ButtonCount = DefButton then begin
gtk_window_set_focus(PGtkWindow(Dialog), AButton);
end;
{ If there is the Cancel button, allow the dialog to close }
if RetValue = IDCANCEL then begin
gtk_object_set_data(PGtkObject(Dialog), 'modal_result', Pointer(IDCANCEL));
end;
gtk_object_set_data(PGtkObject(AButton), 'modal_result',
Pointer(PtrInt(RetValue)));
g_signal_connect(PGtkObject(AButton), 'clicked',
TGtkSignalFunc(@MessageButtonClicked), @ADialogResult);
gtk_container_add (PGtkContainer(PGtkDialog(Dialog)^.action_area), AButton);
end;
begin
ButtonCount:= 0;
{ Determine which is the default button }
DefButton:= ((uType and $00000300) shr 8) + 1;
//DebugLn('Trace:Default button is ' + IntToStr(DefButton));
ADialogResult:= 0;
Dialog:= gtk_dialog_new;
{$IFDEF DebugLCLComponents}
DebugGtkWidgets.MarkCreated(Dialog,'TGtkWidgetSet.MessageBox');
{$ENDIF}
g_signal_connect(PGtkObject(Dialog), 'delete-event', TGtkSignalFunc(@MessageBoxClosed), @ADialogResult);
gtk_window_set_default_size(PGtkWindow(Dialog), 100, 100);
ALabel:= gtk_label_new(lpText);
gtk_container_add (PGtkContainer(PGtkDialog(Dialog)^.vbox), ALabel);
DialogType:= (uType and $0000000F);
if DialogType = MB_OKCANCEL
then begin
CreateButton(PChar(rsMbOK), IDOK);
CreateButton(PChar(rsMbCancel), IDCANCEL);
end
else begin
if DialogType = MB_ABORTRETRYIGNORE
then begin
CreateButton(PChar(rsMbAbort), IDABORT);
CreateButton(PChar(rsMbRetry), IDRETRY);
CreateButton(PChar(rsMbIgnore), IDIGNORE);
end
else begin
if DialogType = MB_YESNOCANCEL
then begin
CreateButton(PChar(rsMbYes), IDYES);
CreateButton(PChar(rsMbNo), IDNO);
CreateButton(PChar(rsMbCancel), IDCANCEL);
end
else begin
if DialogType = MB_YESNO
then begin
CreateButton(PChar(rsMbYes), IDYES);
CreateButton(PChar(rsMbNo), IDNO);
end
else begin
if DialogType = MB_RETRYCANCEL
then begin
CreateButton(PChar(rsMbRetry), IDRETRY);
CreateButton(PChar(rsMbCancel), IDCANCEL);
end
else begin
{ We have no buttons to show. Create the default of OK button }
CreateButton(PChar(rsMbOK), IDOK);
end;
end;
end;
end;
end;
gtk_window_set_title(PGtkWindow(Dialog), lpCaption);
gtk_window_set_position(PGtkWindow(Dialog), GTK_WIN_POS_CENTER);
gtk_window_set_modal(PGtkWindow(Dialog), true);
gtk_widget_show_all(Dialog);
while ADialogResult = 0 do begin
Application.HandleMessage;
end;
DestroyConnectedWidget(Dialog,true);
Result:= ADialogResult;
end;
{------------------------------------------------------------------------------
Function: MoveToEx
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.MoveToEx(DC: HDC; X, Y: Integer;
OldPoint: PPoint): Boolean;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
//DebugLn(Format('trace:> [TGtkWidgetSet.MoveToEx] DC:0x%x, X:%d, Y:%d', [DC, X, Y]));
Result := IsValidDC(DC);
if Result
then with TGtkDeviceContext(DC) do
begin
if OldPoint <> nil then OldPoint^ := PenPos;
if DevCtx.HasTransf then
DevCtx.TransfPoint(X, Y);
PenPos := Point(X, Y);
end;
//DebugLn(Format('trace:< [TGtkWidgetSet.MoveToEx] DC:0x%x, X:%d, Y:%d', [DC, X, Y]));
end;
{------------------------------------------------------------------------------
function MoveWindowOrgEx(DC: HDC; dX, dY: Integer): Boolean; override;
Move the origin of all operations of a DeviceContext.
For example:
Moving the Origin to 10,20 and drawing a point to 50,50, results in
drawing a point to 60,70.
------------------------------------------------------------------------------}
function TGtkWidgetSet.MoveWindowOrgEx(DC: HDC; dX, dY: Integer): Boolean;
var
DevCtx: TGtkDeviceContext absolute DC;
NewOrigin: TPoint;
begin
Result:=IsValidDC(DC);
if Result then
with TGtkDeviceContext(DC) do begin
//DebugLn(['[TGtkWidgetSet.MoveWindowOrgEx] B DC=',DbgS(DC),
// ' Old=',Origin.X,',',Origin.Y,' d=',dX,',',dY,' ']);
NewOrigin:=Origin;
inc(NewOrigin.X,dX);
inc(NewOrigin.Y,dY);
Origin:=NewOrigin;
end;
end;
{------------------------------------------------------------------------------
Method: PaintRgn
Params: DC: HDC; RGN: HRGN
Returns: if the function succeeds
Paints the specified region by using the brush currently selected into the
device context.
------------------------------------------------------------------------------}
function TGtkWidgetSet.PaintRgn(DC: HDC; RGN: HRGN): Boolean;
var
DevCtx: TGtkDeviceContext absolute DC;
CurGdiBrush: PGdiObject;
CurHBrush: HBRUSH absolute CurGdiBrush;
begin
CurGdiBrush := DevCtx.CurrentBrush;
Result := IsValidDC(DC) and IsValidGDIObject(RGN) and IsValidGDIObject(CurHBrush);
if Result then
Result := FillRgn(DC, RGN, CurHBrush);
end;
{------------------------------------------------------------------------------
Function: PeekMessage
Params: lpMsg - Where it should put the message
Handle - Handle of the window (thread)
wMsgFilterMin- Lowest MSG to grab
wMsgFilterMax- Highest MSG to grab
wRemoveMsg - Should message be pulled out of the queue
Returns: Boolean if an event was there
------------------------------------------------------------------------------}
function TGtkWidgetSet.PeekMessage(var lpMsg: TMsg; Handle : HWND;
wMsgFilterMin, wMsgFilterMax, wRemoveMsg: UINT): Boolean;
var
vlItem : TGtkMessageQueueItem;
begin
//TODO Filtering
DebugLn('Peek !!!' );
fMessageQueue.Lock;
try
vlItem := fMessageQueue.FirstMessageItem;
Result := vlItem <> nil;
if Result then begin
lpMsg := vlItem.Msg^;
if (wRemoveMsg and PM_REMOVE) = PM_REMOVE then
fMessageQueue.RemoveMessage(vlItem,FPMF_Internal,true);
end;
finally
fMessageQueue.UnLock;
end;
end;
{------------------------------------------------------------------------------
Method: PolyBezier
Params: DC, Points, NumPts, Filled, Continous
Returns: Boolean
Use Polybezier to draw cubic Bézier curves. The first curve is drawn from the
first point to the fourth point with the second and third points being the
control points. If the Continuous flag is TRUE then each subsequent curve
requires three more points, using the end-point of the previous Curve as its
starting point, the first and second points being used as its control points,
and the third point its end-point. If the continous flag is set to FALSE,
then each subsequent Curve requires 4 additional points, which are used
excatly as in the first curve. Any additonal points which do not add up to
a full bezier(4 for Continuous, 3 otherwise) are ingored. There must be at
least 4 points for an drawing to occur. If the Filled Flag is set to TRUE
then the resulting Poly-Bézier will be drawn as a Polygon.
------------------------------------------------------------------------------}
function TGtkWidgetSet.PolyBezier(DC: HDC; Points: PPoint; NumPts: Integer;
Filled, Continuous: Boolean): Boolean;
begin
Result := inherited PolyBezier(DC, Points, NumPts, Filled, Continuous);
end;
{------------------------------------------------------------------------------
Method: TGtkWidgetSet.Polygon
Params: DC: HDC; Points: ^TPoint; NumPts: integer; Winding: Boolean;
Returns: Nothing
Use Polygon to draw a closed, many-sided shape on the canvas, using the value
of Pen. After drawing the complete shape, Polygon fills the shape using the
value of Brush.
The Points parameter is an array of points that give the vertices of the
polygon.
Winding determines how the polygon is filled. When Winding is True, Polygon
fills the shape using the Winding fill algorithm. When Winding is False,
Polygon uses the even-odd (alternative) fill algorithm.
NumPts indicates the number of points to use.
The first point is always connected to the last point.
To draw a polygon on the canvas, without filling it, use the Polyline method,
specifying the first point a second time at the end.
}
function TGtkWidgetSet.Polygon(DC: HDC; Points: PPoint; NumPts: Integer;
Winding: Boolean): boolean;
var
DevCtx: TGtkDeviceContext absolute DC;
i: integer;
PointArray: PGDKPoint;
Tmp, RGN : hRGN;
ClipRect : TRect;
DCOrigin: TPoint;
OldNumPts: integer;
begin
if not IsValidDC(DC) then Exit(False);
if NumPts <= 0 then Exit(True);
DCOrigin := DevCtx.Offset;
OldNumPts := NumPts;
// create the PointsArray, which is a copy of Points moved by the DCOrigin
// only if needed
if (DevCtx.IsNullPen and (DevCtx.IsNullBrush or Winding)) then
PointArray := nil
else
begin
GetMem(PointArray, SizeOf(TGdkPoint) * (NumPts + 1)); // +1 for return line
for i := 0 to NumPts - 1 do
begin
if DevCtx.HasTransf then
Points[I] := DevCtx.TransfPointIndirect(Points[I]);
PointArray[i].x := Points[i].x + DCOrigin.X;
PointArray[i].y := Points[i].y + DCOrigin.Y;
end;
if (Points[NumPts-1].X <> Points[0].X) or
(Points[NumPts-1].Y <> Points[0].Y) then
begin
// add last point to return to first
PointArray[NumPts].x := PointArray[0].x;
PointArray[NumPts].y := PointArray[0].y;
Inc(NumPts);
end;
end;
// first draw interior in brush color
{$IFDEF DebugGDK}BeginGDKErrorTrap;{$ENDIF}
if not DevCtx.IsNullBrush then
begin
if Winding then
begin
// store old clipping
Tmp := CreateEmptyRegion;
GetClipRGN(DC, Tmp);
// apply new clipping
RGN := CreatePolygonRgn(Points, OldNumPts, LCLType.Winding);
ExtSelectClipRGN(DC, RGN, RGN_AND);
DeleteObject(RGN);
GetClipBox(DC, @ClipRect);
if DevCtx.HasTransf then
begin
ClipRect := DevCtx.InvTransfRectIndirect(ClipRect);
DevCtx.TransfNormalize(ClipRect.Left, ClipRect.Right);
DevCtx.TransfNormalize(ClipRect.Top, ClipRect.Bottom);
end;
// draw polygon area
DevCtx.FillRect(ClipRect, HBrush(PtrUInt(DevCtx.GetBrush)), False);
// restore old clipping
SelectClipRGN(DC, Tmp);
DeleteObject(Tmp);
end
else
begin
DevCtx.SelectBrushProps;
gdk_draw_polygon(DevCtx.Drawable, DevCtx.GC, 1, PointArray, NumPts);
end;
end;
// draw outline
if not DevCtx.IsNullPen
then begin
DevCtx.SelectPenProps;
gdk_draw_polygon(DevCtx.Drawable, DevCtx.GC, 0, PointArray, NumPts);
end;
{$IFDEF DebugGDKTraps}EndGDKErrorTrap;{$ENDIF}
if PointArray <> nil then FreeMem(PointArray);
Result := True;
end;
function TGtkWidgetSet.Polyline(DC: HDC; Points: PPoint; NumPts: Integer): boolean;
var
DevCtx: TGtkDeviceContext absolute DC;
i: integer;
PointArray: PGDKPoint;
DCOrigin: TPoint;
begin
if not IsValidDC(DC) then Exit(False);
if NumPts <= 0 then Exit(True);
if DevCtx.IsNullPen then Exit(True);
DCOrigin := DevCtx.Offset;
GetMem(PointArray, SizeOf(TGdkPoint)*NumPts);
for i:=0 to NumPts-1 do
begin
if DevCtx.HasTransf then
Points[I] := DevCtx.TransfPointIndirect(Points[I]);
PointArray[i].x:=Points[i].x+DCOrigin.X;
PointArray[i].y:=Points[i].y+DCOrigin.Y;
end;
// draw line
DevCtx.SelectPenProps;
Result := dcfPenSelected in DevCtx.Flags;
if Result and not DevCtx.IsNullPen
then begin
{$IFDEF DebugGDK}BeginGDKErrorTrap;{$ENDIF}
gdk_draw_lines(DevCtx.Drawable, DevCtx.GC, PointArray, NumPts);
{$IFDEF DebugGDKTraps}EndGDKErrorTrap;{$ENDIF}
end;
FreeMem(PointArray);
end;
{------------------------------------------------------------------------------
Function: PostMessage
Params: Handle:
Msg:
wParam:
lParam:
Returns: True if succesful
The PostMessage function places (posts) a message in the message queue and
then returns without waiting.
------------------------------------------------------------------------------}
function TGtkWidgetSet.PostMessage(Handle: HWND; Msg: Cardinal; wParam: WParam;
lParam: LParam): Boolean;
function ParentPaintMessageInQueue: boolean;
var
Target: TControl;
Parent: TWinControl;
ParentHandle: hWnd;
begin
Result:=false;
Target:=TControl(GetLCLObject(Pointer(Handle)));
if not (Target is TControl) then exit;
Parent:=Target.Parent;
if (Target is TControl) then begin
Parent:=Target.Parent;
while Parent<>nil do begin
ParentHandle:=Parent.Handle;
if fMessageQueue.FindPaintMessage(ParentHandle)<>nil then begin
Result:=true;
end;
Parent:=Parent.Parent;
end;
end;
end;
procedure CombinePaintMessages(NewMsg:PMsg);
// combine NewMsg and OldMsg paint message into NewMsg and free OldMsg
var
vlItem : TGtkMessageQueueItem;
NewData: TLMGtkPaintData;
OldData: TLMGtkPaintData;
OldMsg : PMsg;
begin
vlItem := fMessageQueue.FindPaintMessage(NewMsg^.Hwnd);
if vlItem = nil then exit;
OldMsg := vlItem.Msg;
if OldMsg = nil then exit;
if (NewMsg^.Message = LM_PAINT) or (OldMsg^.Message = LM_PAINT) then
begin
// LM_PAINT means: repaint all
// convert NewMsg into a LM_PAINT if not already done
if NewMsg^.Message <> LM_PAINT then
begin
FinalizePaintTagMsg(NewMsg);
NewMsg^.Message:=LM_PAINT;
end;
end
else
if (NewMsg^.Message <> LM_GTKPAINT) then
RaiseGDBException('CombinePaintMessages A unknown paint message')
else
if (OldMsg^.Message<>LM_GtkPAINT) then
RaiseGDBException('CombinePaintMessages B unknown paint message')
else
begin
// combine the two LM_GtkPAINT messages
NewData := TLMGtkPaintData(NewMsg^.WParam);
OldData := TLMGtkPaintData(OldMsg^.WParam);
NewData.RepaintAll := NewData.RepaintAll or OldData.RepaintAll;
if not NewData.RepaintAll then
begin
NewData.Rect.Left := Min(NewData.Rect.Left, OldData.Rect.Left);
NewData.Rect.Top := Min(NewData.Rect.Top, OldData.Rect.Top);
NewData.Rect.Right := Max(NewData.Rect.Right, OldData.Rect.Right);
NewData.Rect.Bottom := Max(NewData.Rect.Bottom, OldData.Rect.Bottom);
end;
end;
fMessageQueue.RemoveMessage(vlItem, FPMF_All, True);
end;
var
AMessage: PMsg;
begin
Result := True;
New(AMessage);
FillByte(AMessage^,SizeOf(TMsg),0);
AMessage^.HWnd := Handle; // this is normally the main gtk widget
AMessage^.Message := Msg;
AMessage^.WParam := WParam;
AMessage^.LParam := LParam;
fMessageQueue.Lock;
try
if (AMessage^.Message = LM_PAINT) or (AMessage^.Message = LM_GTKPAINT) then
begin
{ Obsolete, because InvalidateRectangle now works.
// paint messages are the most expensive messages in the LCL
// A paint message to a control will also repaint all child controls.
// -> check if there is already a paint message for one of its parents
// if yes, then skip this message
if ParentPaintMessageInQueue then begin
FinalizePaintTagMsg(AMessage^);
exit;
end;}
// delete old paint message to this widget,
// so that the widget repaints only once
CombinePaintMessages(AMessage);
end;
FMessageQueue.AddMessage(AMessage);
if GetCurrentThreadId <> MainThreadID then
begin
// awake gtk loop
// when the main thread is currently processing messages it will process
// fMessageQueue.
// But when the main thread is waiting for the next gtk message it will
// wait for the next external event before processing fMessageQueue.
// A g_idle_add can only be used if glib multithreading has been enabled
// ToDo: Find out what we loose when enabling multithreading
// or find another way to wake up the gtk loop
{$IFDEF EnabledGtkThreading}
gdk_flush();
g_main_context_wakeup(nil);
{$ELSE}
DebugLn(['TGtkWidgetSet.PostMessage ToDo: wake up gtk']);
{$ENDIF}
end;
finally
fMessageQueue.UnLock;
end;
end;
{------------------------------------------------------------------------------
Method: RadialArc
Params: DC, left, top, right, bottom, sx, sy, ex, ey
Returns: Nothing
Use RadialArc to draw an elliptically curved line with the current Pen. The
values sx,sy, and ex,ey represent the starting and ending radial-points
between which the Arc is drawn.
------------------------------------------------------------------------------}
function TGtkWidgetSet.RadialArc(DC: HDC; left, top, right, bottom,
sx, sy, ex, ey: Integer): Boolean;
begin
Result := inherited RadialArc(DC, left, top, right, bottom, sx, sy, ex, ey);
end;
{------------------------------------------------------------------------------
Method: RadialChord
Params: DC, x1, y1, x2, y2, sx, sy, ex, ey
Returns: Nothing
Use RadialChord to draw a filled Chord-shape on the canvas. The values sx,sy,
and ex,ey represent the starting and ending radial-points between which
the bounding-Arc is drawn.
------------------------------------------------------------------------------}
function TGtkWidgetSet.RadialChord(DC: HDC; x1, y1, x2, y2,
sx, sy, ex, ey: Integer): Boolean;
begin
Result := inherited RadialChord(DC, x1, y1, x2, y2, sx, sy, ex, ey);
end;
{------------------------------------------------------------------------------
Function: RealizePalette
Params: DC: HDC
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.RealizePalette(DC: HDC): Cardinal;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
//DebugLn('Trace:FINISH: [TGtkWidgetSet.RealizePalette]');
Result := 0;
if IsValidDC(DC)
then with TGtkDeviceContext(DC) do
begin
end;
end;
{------------------------------------------------------------------------------
Function: Rectangle
Params: DC: HDC; X1, Y1, X2, Y2: Integer
Returns: Nothing
The Rectangle function draws a rectangle. The rectangle is outlined by using
the current pen and filled by using the current brush.
------------------------------------------------------------------------------}
function TGtkWidgetSet.Rectangle(DC: HDC; X1, Y1, X2, Y2: Integer): Boolean;
var
DevCtx: TGtkDeviceContext absolute DC;
Left, Top, Width, Height: Integer;
DCOrigin: TPoint;
Brush: PGdiObject;
begin
//DebugLn(Format('trace:> [TGtkWidgetSet.Rectangle] DC:0x%x, X1:%d, Y1:%d, X2:%d, Y2:%d', [DC, X1, Y1, X2, Y2]));
if not IsValidDC(DC) then Exit(False);
if DevCtx.HasTransf then
DevCtx.TransfRect(X1, Y1, X2, Y2);
CalculateLeftTopWidthHeight(X1, Y1, X2, Y2, Left, Top, Width, Height);
if (Width = 0) or (Height = 0) then Exit(True);
// X2, Y2 is not part of the rectangle
dec(Width);
dec(Height);
// first draw interior in brush color
DevCtx.SelectBrushProps;
DCOrigin := DevCtx.Offset;
{$IFDEF DebugGDK}BeginGDKErrorTrap;{$ENDIF}
if not DevCtx.IsNullBrush
then begin
Brush := DevCtx.GetBrush;
if (Brush^.GDIBrushFill = GDK_SOLID)
and (IsBackgroundColor(TColor(Brush^.GDIBrushColor.ColorRef)))
then
StyleFillRectangle(DevCtx.Drawable, DevCtx.GC, Brush^.GDIBrushColor.ColorRef,
Left+DCOrigin.X, Top+DCOrigin.Y, Width, Height)
else
gdk_draw_rectangle(DevCtx.Drawable, DevCtx.GC, 1,
Left+DCOrigin.X, Top+DCOrigin.Y, Width, Height);
end;
// Draw outline
DevCtx.SelectPenProps;
Result := dcfPenSelected in DevCtx.Flags;
if Result and not DevCtx.IsNullPen
then gdk_draw_rectangle(DevCtx.Drawable, DevCtx.GC, 0,
Left+DCOrigin.X, Top+DCOrigin.Y, Width, Height);
{$IFDEF DebugGDKTraps}EndGDKErrorTrap;{$ENDIF}
//DebugLn(Format('trace:< [TGtkWidgetSet.Rectangle] DC:0x%x, X1:%d, Y1:%d, X2:%d, Y2:%d', [DC, X1, Y1, X2, Y2]));
end;
{------------------------------------------------------------------------------
Function: RectInRegion
Params: RGN: HRGN; ARect: TRect
Returns: True if any part of the specified rectangle lies within the
boundaries of the region.
Determines whether any part of the specified rectangle is within the boundaries
of a region.
------------------------------------------------------------------------------}
function TGtkWidgetSet.RectInRegion(RGN: HRGN; ARect: TRect): Boolean;
var
AGdkRect: TGdkRectangle;
begin
//todo: sanity checks for valid handle etc.
AGdkRect := GdkRectFromRect(ARect);
Result := gdk_region_rect_in({%H-}PGdiObject(RGN)^.GDIRegionObject, @AGdkRect)
<> GDK_OVERLAP_RECTANGLE_OUT;
end;
{------------------------------------------------------------------------------
Function: RectVisible
Params: dc : hdc; ARect: TRect
Returns: True if ARect is not completely clipped away.
------------------------------------------------------------------------------}
function TGtkWidgetSet.RectVisible(DC: HDC; const ARect: TRect): Boolean;
begin
Result := inherited RectVisible(dc,ARect);
end;
{------------------------------------------------------------------------------
Function: RegroupMenuItem
Params: hndMenu: HMENU; GroupIndex: integer
Returns: Nothing
Move a menuitem into its group
This function is called by the LCL, after some menuitems were regrouped to
GroupIndex. The hndMenu is one of them.
Update all radio groups.
------------------------------------------------------------------------------}
function TGtkWidgetSet.RegroupMenuItem(hndMenu: HMENU;
GroupIndex: Integer): Boolean;
const
GROUPIDX_DATANAME = 'GroupIndex';
function GetGroup: PGSList;
{$IfDef GTK1}
var
Item: PGList;
Arg: TGTKArg;
begin
Result := nil;
Arg.theType := GTK_TYPE_OBJECT;
Arg.Name := 'parent';
gtk_widget_get(Pointer(hndMenu), @Arg);
if Arg.d.object_data = nil then Exit;
Item := gtk_container_children(PGTKContainer(Arg.d.object_data));
while Item <> nil do
begin
if (Item^.Data <> Pointer(hndMenu)) // exclude ourself
and gtk_is_radio_menu_item(Item^.Data)
and (PtrUInt(GroupIndex) = PtrUInt(gtk_object_get_data(Item^.Data, GROUPIDX_DATANAME)))
then begin
Result := gtk_radio_menu_item_group(PGtkRadioMenuItem(Item^.Data));
Exit;
end;
Item := Item^.Next;
end;
{$Else}
var
Item: PGList;
parent : PGTKWidget;
begin
Result := nil;
parent := gtk_widget_get_parent(Pointer(hndMenu));
if parent = nil then Exit;
Item := gtk_container_children(PGTKContainer(parent));
while Item <> nil do
begin
if (Item^.Data <> Pointer(hndMenu)) // exclude ourself
and gtk_is_radio_menu_item(Item^.Data)
and (GroupIndex = Integer(PtrUInt(gtk_object_get_data(Item^.Data, GROUPIDX_DATANAME))))
then begin
Result := gtk_radio_menu_item_get_group (PGtkRadioMenuItem(Item^.Data));
Exit;
end;
Item := Item^.Next;
end;
{$EndIf}
end;
var
RadioGroup: PGSList;
CurrentGroupIndex: Integer;
begin
Result := False;
if not gtk_is_radio_menu_item(Pointer(hndMenu))
then begin
DebugLn('WARNING: TGtkWidgetSet.RegroupMenuItem: handle is not a GTK_RADIO_MENU_ITEM');
Exit;
end;
CurrentGroupIndex := integer(PtrUInt(gtk_object_get_data(Pointer(hndMenu), GROUPIDX_DATANAME)));
// Update needed ?
if GroupIndex = CurrentGroupIndex
then begin
Result := True;
Exit;
end;
// Remove current group
gtk_radio_menu_item_set_group(PGtkRadioMenuItem(hndMenu), nil);
gtk_object_set_data(Pointer(hndMenu), GROUPIDX_DATANAME, nil);
// Check remove only
if GroupIndex = 0
then begin
Result := True;
Exit;
end;
// Try to find new group
RadioGroup := GetGroup;
// Set new group
gtk_object_set_data(Pointer(hndMenu), GROUPIDX_DATANAME, Pointer(PtrInt(GroupIndex)));
if RadioGroup = nil
then begin
// We're the only member, get a group
RadioGroup := gtk_radio_menu_item_group(PGtkRadioMenuItem(hndMenu))
end
else begin
gtk_radio_menu_item_set_group(PGtkRadioMenuItem(hndMenu), RadioGroup);
end;
//radiogroup^.data
//radiogroup^.next
// Refetch newgroup list
RadioGroup := gtk_radio_menu_item_group(PGtkRadioMenuItem(hndMenu));
// Update checks
UpdateRadioGroupChecks(RadioGroup);
Result := True;
end;
{------------------------------------------------------------------------------
Function: ReleaseCapture
Params: none
Returns: True if succesful
The ReleaseCapture function releases the mouse capture from a window
and restores normal mouse input processing.
------------------------------------------------------------------------------}
function TGtkWidgetSet.ReleaseCapture: Boolean;
begin
SetCapture(0);
Result := True;
end;
{------------------------------------------------------------------------------
Function: ReleaseDC
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.ReleaseDC(hWnd: HWND; DC: HDC): Integer;
var
DevCtx: TGtkDeviceContext absolute DC;
aDC, pSavedDC: TGtkDeviceContext;
g: TGDIType;
CurGDIObject: PGDIObject;
begin
//DebugLn(['[TGtkWidgetSet.ReleaseDC] ',DC,' ',FDeviceContexts.Count]);
//DebugLn(Format('trace:> [TGtkWidgetSet.ReleaseDC] DC:0x%x', [DC]));
Result := 0;
if (DC <> 0)
then begin
if FDeviceContexts.Contains(Pointer(DC))
then begin
aDC := TGtkDeviceContext(DC);
// clear references to all GDI objects
for g:=Low(TGDIType) to high(TGDIType) do begin
{if aDC.GDIObjects[g]<>nil then
if FindDCWithGDIObject(aDC.GDIObjects[g])=nil then
RaiseGDBException('');}
aDC.GDIObjects[g]:=nil; // clear the reference, decrease DCCount
end;
// Release all saved device contexts (the owned GDI objects will be freed)
pSavedDC:=aDC.SavedContext;
if pSavedDC<>nil then begin
ReleaseDC(0,HDC(pSavedDC));
aDC.SavedContext:=nil;
end;
//DebugLn(['TGtkWidgetSet.ReleaseDC DC=',dbgs(TGtkDeviceContext(aDC)),' ClipRegion=',dbgs(aDC.ClipRegion)]);
// free all owned GDI objects
for g:=Low(TGDIType) to high(TGDIType) do begin
CurGDIObject:=aDC.OwnedGDIObjects[g];
if CurGDIObject<>nil then begin
if CurGDIObject^.Owner<>aDC then
RaiseGDBException('');
DeleteObject(HGDIOBJ(PtrUInt(CurGDIObject)));
if aDC.OwnedGDIObjects[g]<>nil then
RaiseGDBException('');
end;
end;
//DebugLn(['TGtkWidgetSet.ReleaseDC DC=',dbghex(PtrInt(DC)),' Font=',dbghex(PtrInt(aDC.CurrentFont))]);
{FreeGDIColor(aDC.CurrentTextColor);
FreeGDIColor(aDC.CurrentBackColor);}
try
{ On root window, we don't allocate a graphics context and so we do not free}
if aDC.HasGC then
begin
gdk_gc_unref(aDC.GC);
aDC.GC:=nil;
end;
except
on E:Exception do begin
// Nothing, just try to unref it
// (it segfaults if the window doesnt exist anymore :-)
DebugLn('TGtkWidgetSet.ReleaseDC: ',E.Message);
end;
end;
DisposeDC(aDC);
Result := 1;
end;
end;
//DebugLn(Format('trace:< [TGtkWidgetSet.ReleaseDC] FDeviceContexts DC:0x%x', [DC]));
end;
{------------------------------------------------------------------------------
Function: RemoveProp
Params: Handle: Handle of the object
Str: Name of the property to remove
Returns: The handle of the property (0=failure)
------------------------------------------------------------------------------}
function TGtkWidgetSet.RemoveProp(Handle: hwnd; Str: PChar): THandle;
begin
gtk_object_remove_data(pGTKObject(handle), Str);
Result := 1;
end;
{------------------------------------------------------------------------------
Function: RestoreDC
Params: none
Returns: Nothing
-------------------------------------------------------------------------------}
function TGtkWidgetSet.RestoreDC(DC: HDC; SavedDC: Integer): Boolean;
var
DevCtx: TGtkDeviceContext absolute DC;
SavedDevCtx: TGtkDeviceContext;
ClipRegionChanged: Boolean;
begin
//DebugLn(Format('Trace:> [TGtkWidgetSet.RestoreDC] DC:0x%x, SavedDC: %d', [DC, SavedDC]));
if not IsValidDC(DC) then Exit(False);
if SavedDC <= 0 then Exit(False);
repeat
SavedDevCtx := DevCtx.SavedContext;
Dec(SavedDC);
// TODO copy bitmap too
ClipRegionChanged := DevCtx.ClipRegion <> SavedDevCtx.ClipRegion;
// clear the GDIObjects in pSavedDC, so they are not freed by DeleteDC
Result := DevCtx.CopyDataFrom(SavedDevCtx, True, True, True);
DevCtx.SavedContext := SavedDevCtx.SavedContext;
SavedDevCtx.SavedContext := nil;
if ClipRegionChanged then
DevCtx.SelectRegion;
// free saved DC
DeleteDC(HDC(SavedDevCtx));
until SavedDC <= 0;
//DebugLn(Format('Trace:< [TGtkWidgetSet.RestoreDC] DC:0x%x, Saved: %d --> %s', [Integer(DC), SavedDC, BOOL_TEXT[Result]]));
end;
{------------------------------------------------------------------------------
Method: RoundRect
Params: X1, Y1, X2, Y2, RX, RY
Returns: If succesfull
Draws a Rectangle with optional rounded corners. RY is the radial height
of the corner arcs, RX is the radial width. If either is less than or equal to
0, the routine simly calls to standard Rectangle.
------------------------------------------------------------------------------}
function TGtkWidgetSet.RoundRect(DC : hDC; X1, Y1, X2, Y2: Integer;
RX,RY : Integer): Boolean;
begin
Result := inherited RoundRect(DC, X1, Y1, X2, Y2, RX, RY);
end;
{------------------------------------------------------------------------------
Function: SaveDc
Params: DC: a DC to save
Returns: 0 if the functions fails otherwise a positive integer identifing
the saved DC
The SaveDC function saves the current state of the specified device
context (DC) by copying its elements to a context stack.
-------------------------------------------------------------------------------}
function TGtkWidgetSet.SaveDC(DC: HDC): Integer;
var
DevCtx: TGtkDeviceContext absolute DC;
aSavedDC: TGtkDeviceContext;
begin
//DebugLn(Format('Trace:> [TGtkWidgetSet.SaveDC] 0x%x', [Integer(DC)]));
Result := 0;
if IsValidDC(DC) then
begin
aSavedDC := NewDC;
aSavedDC.CopyDataFrom(DevCtx, False, True, False);
aSavedDC.SavedContext := DevCtx.SavedContext;
DevCtx.SavedContext:= aSavedDC;
Result := 1;
end;
//DebugLn(Format('Trace:< [TGtkWidgetSet.SaveDC] 0x%x --> %d', [Integer(DC), Result]));
end;
{------------------------------------------------------------------------------
Function: ScreenToClient
Params: Handle:
P:
Returns:
------------------------------------------------------------------------------}
function TGtkWidgetSet.ScreenToClient(Handle : HWND; var P : TPoint) : Integer;
var
X, Y: Integer;
Widget: PGTKWidget;
Window: PgdkWindow;
Begin
if Handle = 0
then begin
X := 0;
Y := 0;
end
else
begin
Widget := GetFixedWidget(pgtkwidget(Handle));
if Widget = nil then
Widget := pgtkwidget(Handle);
if Widget = nil then
begin
X := 0;
Y := 0;
end
else begin
Window:=GetControlWindow(Widget);
{$IFDEF DebugGDK}BeginGDKErrorTrap;{$ENDIF}
if Window<>nil then
gdk_window_get_origin(Window, @X, @Y)
else begin
X:=0;
Y:=0;
end;
{$IFDEF DebugGDKTraps}EndGDKErrorTrap;{$ENDIF}
end;
end;
//DebugLn('[TGtkWidgetSet.ScreenToClient] ',x,',',y,' P=',P.X,',',P.Y);
dec(P.X, X);
dec(P.Y, Y);
Result := -1;
end;
{------------------------------------------------------------------------------
Function: ScrollWindowEx
Params: hWnd: handle of window to scroll
dx: horizontal amount to scroll
dy: vertical amount to scroll
prcScroll: pointer to scroll rectangle
prcClip: pointer to clip rectangle
hrgnUpdate: handle of update region
prcUpdate: pointer to update rectangle
flags: scrolling flags
Returns: True if succesfull;
The ScrollWindowEx function scrolls the content of the specified window's
client area
------------------------------------------------------------------------------}
function TGtkWidgetSet.ScrollWindowEx(hWnd: HWND; dx, dy: Integer; prcScroll, prcClip: PRect; hrgnUpdate: HRGN; prcUpdate: PRect; flags: UINT): Boolean;
begin
Result := False;
end;
{------------------------------------------------------------------------------
Function: SelectClipRGN
Params: DC, RGN
Returns: longint
Sets the DeviceContext's ClipRegion. The Return value
is the new clip regions type, or ERROR.
The result can be one of the following constants
Error
NullRegion
SimpleRegion
ComplexRegion
------------------------------------------------------------------------------}
function TGtkWidgetSet.SelectClipRGN(DC : hDC; RGN : HRGN) : Longint;
var
DevCtx: TGtkDeviceContext absolute DC;
RegObj: PGdkRegion;
DCOrigin: TPoint;
OldClipRegion: PGDIObject;
begin
if not IsValidDC(DC) then Exit(ERROR);
// clear old clipregion
if DevCtx.ClipRegion <> nil
then begin
OldClipRegion := DevCtx.ClipRegion;
DevCtx.ClipRegion := nil;// decrease DCCount
if OldClipRegion = DevCtx.OwnedGDIObjects[gdiRegion]
then DeleteObject(HGDIOBJ(PtrUInt(OldClipRegion)));
end;
if RGN = 0
then begin
DevCtx.SelectRegion;
Exit(NULLREGION);
end;
if IsValidGDIObject(RGN)
then begin
DevCtx.ClipRegion := PGdiObject(CreateRegionCopy(RGN));
DevCtx.OwnedGDIObjects[gdiRegion] := DevCtx.ClipRegion;
RegObj := DevCtx.ClipRegion^.GDIRegionObject;
DCOrigin := DevCtx.Offset;
gdk_region_offset(RegObj, DCOrigin.x, DCOrigin.Y);
DevCtx.SelectRegion;
Exit(RegionType(RegObj));
end;
// error handling
Result := ERROR;
DebugLn('WARNING: [TGtkWidgetSet.SelectClipRGN] Invalid RGN');
{$ifdef TraceGdiCalls}
DebugLn();
DebugLn('TraceCall for invalid object: ');
DumpBackTrace(PgdiObject(RGN)^.StackAddrs);
DebugLn();
{$endif}
end;
{------------------------------------------------------------------------------
Function: SelectObject
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.SelectObject(DC: HDC; GDIObj: HGDIOBJ): HGDIOBJ;
var
DevCtx: TGtkDeviceContext absolute DC;
GDIObject: PGdiObject absolute GDIObj;
ResultObj: PGdiObject absolute Result;
procedure RaiseInvalidGDIType;
begin
RaiseGDBException('TGtkWidgetSet.SelectObject Invalid GDIType '+IntToStr(ord(PGdiObject(GDIObj)^.GDIType)));
end;
{$ifdef DebugLCLComponents}
procedure DebugInvalidDC;
begin
DebugLn(['TGtkWidgetSet.SelectObject DC=',dbghex(DC),' IsValidDC(DC)=',IsValidDC(DC),' GDIObj=',dbghex(GDIObj)]);
DumpStack;
DebugLn(['DebugInvalidGDIObject DC:']);
Debugln(DebugDeviceContexts.GetInfo(Pointer(DC),true));
end;
procedure DebugInvalidGDIObject;
begin
DebugLn(['TGtkWidgetSet.SelectObject DC=',dbghex(DC),' GDIObj=',dbghex(GDIObj),' IsValidGDIObject(GDIObj)=',IsValidGDIObject(GDIObj)]);
DumpStack;
DebugLn(['DebugInvalidGDIObject GDIObj:']);
Debugln(DebugGdiObjects.GetInfo(Pointer(GDIObj),true));
end;
{$endif}
begin
Result := 0;
if not IsValidDC(DC)
then begin
{$ifdef DebugLCLComponents}
DebugInvalidDC;
{$endif}
Exit;
end;
if not IsValidGDIObject(GDIObj)
then begin
{$ifdef DebugLCLComponents}
DebugInvalidGDIObject;
{$endif}
Exit;
end;
case GDIObject^.GDIType of
gdiPen,
gdiBitmap:
ResultObj := DevCtx.SelectObject(GDIObject);
gdiBrush: begin
//DebugLn(Format('trace: [TGtkWidgetSet.SelectObject] DC: 0x%x, Type: Brush', [DC]));
ResultObj := DevCtx.GetBrush;// always create, because a valid GDIObject is needed to restore
if DevCtx.CurrentBrush = GDIObject then Exit;
DevCtx.CurrentBrush := GDIObject;
DevCtx.SelectedColors := dcscCustom;
if DevCtx.GC = nil then Exit;
gdk_gc_set_fill(DevCtx.GC, GDIObject^.GDIBrushFill);
case GDIObject^.GDIBrushFill of
GDK_STIPPLED: gdk_gc_set_stipple(DevCtx.GC, GDIObject^.GDIBrushPixMap);
GDK_TILED: gdk_gc_set_tile(DevCtx.GC, GDIObject^.GDIBrushPixMap);
end;
end;
gdiFont: begin
//DebugLn(Format('trace: [TGtkWidgetSet.SelectObject] DC: 0x%x, Type: Font', [DC]));
ResultObj := DevCtx.GetFont;// always create, because a valid GDIObject is needed to restore
if (DevCtx.CurrentFont = GDIObject) and not DevCtx.HasTransf then Exit;
DevCtx.CurrentFont := GDIObject;
{$ifdef GTK1}
if DevCtx.GC <> nil then
gdk_gc_set_font(DevCtx.GC, GdiObject^.GDIFontObject);
{$endif}
DevCtx.SetTextMetricsValid(False);
DevCtx.SelectedColors := dcscCustom;
end;
gdiRegion: begin
ResultObj := DevCtx.ClipRegion;
if DevCtx.GC <> nil
then SelectClipRGN(DC, GDIObj)
else DevCtx.ClipRegion := nil;
end;
else
RaiseInvalidGDIType;
end;
end;
{------------------------------------------------------------------------------
Function: SelectPalette
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.SelectPalette(DC: HDC; Palette: HPALETTE; ForceBackground: Boolean): HPALETTE;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
//DebugLn('Trace:TODO: [TGtkWidgetSet.SelectPalette]');
//TODO: Implement this;
Result := 0;
end;
{------------------------------------------------------------------------------
Function: SendMessage
Params: hWnd:
Msg:
wParam:
lParam:
Returns:
The SendMessage function sends the specified message to a window or windows.
The function calls the window procedure for the specified window and does
not return until the window procedure has processed the message.
------------------------------------------------------------------------------}
function TGtkWidgetSet.SendMessage(HandleWnd: HWND; Msg: Cardinal; wParam: WParam;
lParam: LParam): LResult;
var
OldMsg: Cardinal;
procedure PreparePaintMessage(TargetObject: TObject; var AMessage: TLMessage);
var
GtkPaintData: TLMGtkPaintData;
OldGtkPaintMsg: TLMGtkPaint;
{$IFNDEF Gtk2}
PaintDC: HDC;
DCOrigin: TPoint;
{$ENDIF}
begin
(* MG: old trick. Not used anymore, but it might be, that someday there
will be component, that works better with this, so it is kept.
{ The LCL repaints controls in a top-down hierachy. But the gtk sends
gtkdraw events bottom-up. So, controls at the bottom are repainted
many times. To avoid this the queue is checked for LM_PAINT messages
for the parent control. If there is a parent LM_PAINT, this message
is ignored.}
if (Target is TControl) then begin
ParentControl:=TControl(Target).Parent;
while ParentControl<>nil do begin
ParentHandle:=TWinControl(ParentControl).Handle;
if FindPaintMessage(ParentHandle)<>nil then begin
{$IFDEF VerboseDsgnPaintMsg}
if (csDesigning in TComponent(Target).ComponentState) then begin
DebugLn('TGtkWidgetSet.SendMessage A ',
TComponent(Target).Name,':',Target.ClassName,
' Parent Message found: ',ParentControl.Name,':',ParentControl.ClassName
);
end;
{$ENDIF}
if Msg=LM_PAINT then
ReleaseDC(0,AMessage.WParam);
//exit;
end;
ParentControl:=ParentControl.Parent;
end;
end; *)
{$IFDEF VerboseDsgnPaintMsg}
if (csDesigning in TComponent(TargetObject).ComponentState) then begin
write('TGtkWidgetSet.SendMessage B ',
TComponent(TargetObject).Name,':',TargetObject.ClassName,
' GtkPaint=',AMessage.Msg=LM_GtkPAINT);
if AMessage.Msg=LM_GtkPAINT then begin
if AMessage.wParam<>0 then begin
with TLMGtkPaintData(AMessage.wParam) do begin
write(' GtkPaintData(',
' Widget=',DbgS(Widget),'=',GetWidgetClassName(Widget),
' State=',State,
' Rect=',Rect.Left,',',Rect.Top,',',Rect.Right,',',Rect.Bottom,
' RepaintAll=',RepaintAll,
')');
end;
end else begin
write(' GtkPaintData=nil');
end;
end;
DebugLn('');
end;
{$ENDIF}
if AMessage.Msg = LM_GTKPAINT
then begin
OldGtkPaintMsg := TLMGtkPaint(AMessage);
GtkPaintData := OldGtkPaintMsg.Data;
// convert LM_GTKPAINT to LM_PAINT
AMessage := TLMessage(GtkPaintMessageToPaintMessage(
TLMGtkPaint(AMessage), False));
{$IfNDef GTK2}
if (GtkPaintData <> nil) and (not GtkPaintData.RepaintAll)
then begin
PaintDC := TLMPaint(AMessage).DC;
DCOrigin := TGtkDeviceContext(PaintDC).Offset;
with GtkPaintData.Rect do
IntersectClipRect(PaintDC,
Left - DCOrigin.X, Top - DCOrigin.Y,
Right - DCOrigin.X, Bottom - DCOrigin.Y);
end;
{$EndIf}
GtkPaintData.Free;
end;
end;
procedure DisposePaintMessage(TargetObject: TObject; var AMessage: TLMessage);
begin
if OldMsg = LM_GTKPAINT then
begin
FinalizePaintMessage(@AMessage);
end
else
if (AMessage.Msg = LM_PAINT) and (AMessage.WParam <> 0) then
begin
// free DC
ReleaseDC(0, AMessage.WParam);
AMessage.WParam := 0;
end;
end;
var
AMessage: TLMessage;
Target: TObject;
begin
OldMsg := Msg;
AMessage.Msg := Msg;
AMessage.WParam := WParam;
AMessage.LParam := LParam;
AMessage.Result := 0;
Target := GetLCLObject(Pointer(HandleWnd));
if Target <> nil then
begin
if (Msg = LM_PAINT) or (Msg = LM_GTKPAINT) then
begin
PreparePaintMessage(Target,AMessage);
Result := DoDeliverPaintMessage(Target, TLMPaint(AMessage));
end
else
Result := DeliverMessage(Target, AMessage); // deliver it
if (Msg = LM_PAINT) or (Msg = LM_GTKPAINT) then
DisposePaintMessage(Target, AMessage);
end;
end;
{------------------------------------------------------------------------------
function SetActiveWindow(Handle: HWND): HWND;
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetActiveWindow(Handle: HWND): HWND;
begin
// ToDo
Result := GetActiveWindow;
{$ifdef gtk2}
if (Handle <> 0) and GtkWidgetIsA(PGtkWidget(Handle),GTK_TYPE_WINDOW) then
begin
if GTK_WIDGET_VISIBLE(PGtkWidget(Handle)) then
gtk_window_present(PGtkWindow(Handle));
end else
Result := 0; // if not active window return error
{$endif}
end;
{------------------------------------------------------------------------------
Function: SetBkColor pbd
Params: DC: Device context to change the text background color
Color: RGB Tuple
Returns: Old Background color
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetBKColor(DC: HDC; Color: TColorRef): TColorRef;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
//DebugLn(Format('trace:> [TGtkWidgetSet.SetBKColor] DC: 0x%x Color: %8x', [Integer(DC), Color]));
Result := CLR_INVALID;
if IsValidDC(DC)
then begin
with TGtkDeviceContext(DC) do
begin
Result := CurrentBackColor.ColorRef;
SetGDIColorRef(CurrentBackColor,Color);
end;
end;
//DebugLn(Format('trace:< [TGtkWidgetSet.SetBKColor] DC: 0x%x Color: %8x --> %8x', [Integer(DC), Color, Result]));
end;
{------------------------------------------------------------------------------
Function: SetBkMode
Params: DC:
bkMode:
Returns:
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetBkMode(DC: HDC; bkMode : Integer) : Integer;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
// Your code here
Result:=0;
end;
{------------------------------------------------------------------------------
function TGtkWidgetSet.SetComboMinDropDownSize(Handle: HWND;
MinItemsWidth, MinItemsHeight: integer): boolean;
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetComboMinDropDownSize(Handle: HWND;
MinItemsWidth, MinItemsHeight, MinItemCount: integer): boolean;
var
ComboWidget: PGtkCombo;
DropDownWidget, ListWidget, FirstChildWidget: PGtkWidget;
FirstChild: PGList;
CurX, CurY, CurWidth, CurHeight, CurItemHeight, BorderX, BorderY,
OldWidth, OldHeight,
NewWidth, NewHeight: integer;
ComboPopup: PGtkScrolledWindow;
item_requisition: TGtkRequisition;
begin
Result:=true;
if not (GtkWidgetIsA(PgtkWidget(Handle),GTK_TYPE_COMBO)) then
RaiseGDBException('TGtkWidgetSet.SetComboMinDropDownSize invalid handle');
// get current items width and height
ComboWidget:=PGtkCombo(Handle);
ListWidget:=ComboWidget^.List;
if ListWidget=nil then exit;
CurWidth:=ListWidget^.Allocation.Width;
// CurHeight:=ListWidget^.Allocation.Height;
CurHeight:=ListWidget^.requisition.Height;
if MinItemCount>0 then begin
FirstChild:=PGTkList(ListWidget)^.children;
if FirstChild<>nil then begin
FirstChildWidget:=PGtkWidget(FirstChild^.Data);
if FirstChildWidget<>nil then begin
gtk_widget_size_request(FirstChildWidget,@item_requisition);
CurItemHeight:=Max(FirstChildWidget^.Allocation.Height,
item_requisition.Height);
end else begin
CurItemHeight:=1;
end;
if MinItemsHeight<CurItemHeight*MinItemCount then
MinItemsHeight:=CurItemHeight*MinItemCount;
end;
end;
// calculate new width and height
DropDownWidget:=ComboWidget^.popwin;
if DropDownWidget=nil then exit;
ComboPopup:=PGtkScrolledWindow(ComboWidget^.popup);
if ComboPopup=nil then exit;
CurX:=DropDownWidget^.Allocation.x;
CurY:=DropDownWidget^.Allocation.y;
CurWidth:=pGtkWidget(ComboPopup)^.allocation.Width;
CurHeight:=pGtkWidget(ComboPopup)^.allocation.Height;
OldWidth:=DropDownWidget^.allocation.Width;
OldHeight:=DropDownWidget^.allocation.Height;
BorderX:=2*(OldWidth-CurWidth);
if BorderX<0 then BorderX:=0;
BorderY:=2*(OldHeight-CurHeight);
if BorderY<0 then BorderY:=0;
if Gtk_Widget_visible(ComboPopup^.hscrollbar) then
inc(BorderY, ComboPopup^.hscrollbar^.requisition.height
+GTK_SCROLLED_WINDOW_CLASS(gtk_object_get_class(comboWidget^.popup))^.scrollbar_spacing);
if Gtk_Widget_visible(ComboPopup^.vscrollbar) then
inc(BorderX,ComboPopup^.vscrollbar^.requisition.width
+GTK_SCROLLED_WINDOW_CLASS(gtk_object_get_class(comboWidget^.popup))^.scrollbar_spacing);
if minItemsWidth <>0 then NewWidth := MinItemsWidth+BorderX
else NewWidth := OldWidth;
if minItemsHeight<>0 then NewHeight := MinItemsHeight+BorderY
else NewHeight := OldHeight;
if (NewWidth=OldWidth) and (NewHeight=OldHeight) then exit;
NewWidth:=Min(NewWidth, Screen.Width - CurX);
NewHeight:=Min(NewHeight, Screen.Height - CurY);
if assigned(dropdownWidget^.Window) then
// widget is realized, resize gdkwindow directly
gdk_window_resize(dropdownwidget^.Window,newWidth,newHeight)
else
// widget is not yet realized, force resize needed for shrinking under gtk1)
gtk_widget_set_usize(PGtkWidget(dropDownWidget), -1,-1);
end;
{------------------------------------------------------------------------------
Function: SetCapture
Params: Value: Handle of window to capture
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetCapture(AHandle: HWND): HWND;
var
Widget: PGtkWidget;
begin
//DebugLn(Format('Trace:> [TGtkWidgetSet.SetCapture] 0x%x', [AHandle]));
Widget := PGtkWidget(AHandle);
{$IfDef VerboseMouseCapture}
DebugLn('TGtkWidgetSet.SetCapture NewValue=[',GetWidgetDebugReport(Widget),']');
{$EndIf}
// return old capture handle
Result := GetCapture;
// capture
CaptureMouseForWidget(Widget, mctLCL);
end;
{------------------------------------------------------------------------------
Function: SetCaretPos
Params: new position x, y
Returns: true on success
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetCaretPos(X, Y: Integer): Boolean;
var
FocusObject: PGTKObject;
begin
FocusObject := PGTKObject(GetFocus);
Result:=SetCaretPosEx(PtrUInt(FocusObject),X,Y);
end;
{------------------------------------------------------------------------------
Function: SetCaretPos
Params: new position x, y
Returns: true on success
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetCaretPosEx(Handle: HWNd; X, Y: Integer): Boolean;
var
GtkObject: PGTKObject;
begin
GtkObject := PGTKObject(Handle);
Result := GtkObject <> nil;
if Result then begin
if gtk_type_is_a(gtk_object_type(GtkObject), GTKAPIWidget_GetType)
then begin
GTKAPIWidget_SetCaretPos(PGTKAPIWidget(GtkObject), X, Y);
end
// else if // TODO: other widgettypes
else begin
Result := False;
end;
end;
end;
{------------------------------------------------------------------------------
Function: SetCaretRespondToFocus
Params: handle : Handle of a TWinControl
ShowHideOnFocus: true = caret is hidden on focus lost
Returns: true on success
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetCaretRespondToFocus(handle: HWND;
ShowHideOnFocus: boolean): Boolean;
begin
if handle<>0 then begin
if gtk_type_is_a(gtk_object_type(PGTKObject(handle)), GTKAPIWidget_GetType)
then begin
GTKAPIWidget_SetCaretRespondToFocus(PGTKAPIWidget(handle),
ShowHideOnFocus);
Result:=true;
end
else begin
Result := False;
end;
end else
Result:=false;
end;
{------------------------------------------------------------------------------
Function: SetCursor
Params : hCursor - cursor handle
Returns : current cursor
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetCursor(ACursor: HCURSOR): HCURSOR;
var
DefaultCursor: HCursor;
procedure SetGlobalCursor;
var
TopList, List: PGList;
begin
TopList := gdk_window_get_toplevels;
List := TopList;
while List <> nil do
begin
if (List^.Data <> nil) then
SetWindowCursor(PGDKWindow(List^.Data), ACursor, True);
list := g_list_next(list);
end;
if TopList <> nil then
g_list_free(TopList);
end;
procedure ResetGlobalCursor;
procedure SetToWindow(AWindow: PGDKWindow);
var
data: gpointer;
Widget: PGTKWidget absolute data;
WidgetInfo: PWidgetInfo;
WSPrivate: TWSPrivateClass;
begin
gdk_window_get_user_data(AWindow, @data);
if GtkWidgetIsA(Widget, gtk_widget_get_type)
then begin
WidgetInfo := GetWidgetInfo(Widget);
if (WidgetInfo <> nil)
and (WidgetInfo^.LCLObject <> nil)
and (WidgetInfo^.LCLObject is TWinControl)
then begin
WSPrivate := TWinControl(WidgetInfo^.LCLObject).WidgetSetClass.WSPrivate;
TGtkPrivateWidgetClass(WSPrivate).UpdateCursor(WidgetInfo);
Exit;
end;
end;
// no lcl cursor, so reset to default
//gdk_window_set_cursor(AWindow, PGdkCursor(DefaultCursor));
SetWindowCursor(AWindow, DefaultCursor, True);
end;
procedure Traverse(AWindow: PGDKWindow);
var
ChildWindows, ListEntry: PGList;
begin
SetToWindow(AWindow);
ChildWindows := gdk_window_get_children(AWindow);
ListEntry := ChildWindows;
while ListEntry <> nil do
begin
Traverse(PGdkWindow(ListEntry^.Data));
ListEntry := ListEntry^.Next;
end;
g_list_free(ChildWindows);
end;
var
TopList, List: PGList;
begin
TopList := gdk_window_get_toplevels;
List := TopList;
while List <> nil do
begin
if (List^.Data <> nil) then
Traverse(PGDKWindow(List^.Data));
list := g_list_next(list);
end;
if TopList <> nil then
g_list_free(TopList);
end;
begin
// set global gtk cursor
Result := FGlobalCursor;
if ACursor = FGlobalCursor then Exit;
DefaultCursor := Screen.Cursors[crDefault];
if ACursor <> DefaultCursor
then SetGlobalCursor
else ResetGlobalCursor;
FGlobalCursor := ACursor;
end;
{------------------------------------------------------------------------------
Function: SetCursorPos
Params: X:
Y:
Returns:
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetCursorPos(X, Y: Integer): Boolean;
{$IFDEF HasX}
var
dpy: PDisplay;
begin
Result := False;
{$IFDEF DebugGDKTraps}
BeginGDKErrorTrap;
{$ENDIF}
try
dpy := gdk_display;
XWarpPointer(dpy, 0, RootWindow(dpy, DefaultScreen(dpy)), 0, 0, 0, 0, X, Y);
Result := True;
XFlush(dpy);
finally
{$IFDEF DebugGDKTraps}EndGDKErrorTrap;{$ENDIF}
end;
end;
{$ELSE HasX}
begin
Result := False;
DebugLn('TGtkWidgetSet.SetCursorPos not implemented for this platform');
// Can this call TWin32WidgetSet.SetCursorPos?
end;
{$ENDIF HasX}
{------------------------------------------------------------------------------
Function: SetFocus
Params: hWnd: Handle of new focus window
Returns: The old focus window
The SetFocus function sets the keyboard focus to the specified window
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetFocus(hWnd: HWND): HWND;
{off $DEFINE VerboseFocus}
var
Widget, TopLevel, NewFocusWidget: PGtkWidget;
Info: PWidgetInfo;
{$IfDef VerboseFocus}
AWinControl: TWinControl;
{$EndIf}
NewTopLevelWidget: PGtkWidget;
NewTopLevelObject: TObject;
NewForm: TCustomForm;
begin
if hwnd = 0 then
begin
Result:=0;
exit;
end;
Widget:=PGtkWidget(hWnd);
{$IfDef VerboseFocus}
DebugLn('');
debugln('[TGtkWidgetSet.SetFocus] A hWnd=',GetWidgetDebugReport(Widget));
//DebugLn(getStackTrace(true));
{$EndIf}
// return the old focus handle
Result := GetFocus;
NewFocusWidget := nil;
TopLevel := gtk_widget_get_toplevel(Widget);
{$IfDef VerboseFocus}
Debugln('[TGtkWidgetSet.SetFocus] B');
DbgOut(' TopLevel=',DbgS(TopLevel));
DbgOut(' OldFocus=',GetWidgetDebugReport(PGtkWidget(Result)));
DebugLn('');
if not GTK_WIDGET_VISIBLE(Widget) then
raise Exception.Create('TGtkWidgetSet.SetFocus: Widget is not visible');
{$EndIf}
if Result=hWnd then exit;
if GtkWidgetIsA(TopLevel, gtk_window_get_type) then
begin
// TopLevel is a gtkwindow
{$IfDef VerboseFocus}
AWinControl:=TWinControl(GetNearestLCLObject(PGtkWindow(TopLevel)^.focus_widget));
write(' C TopLevel is a gtkwindow ');
write(' focus_widget=',DbgS(PGtkWindow(TopLevel)^.focus_widget));
if AWinControl<>nil then
write(' LCLParent=',AWinControl.Name,':',AWinControl.ClassName)
else
write(' LCLParent=nil');
DebugLn('');
{$EndIf}
NewTopLevelObject:=GetNearestLCLObject(TopLevel);
if (NewTopLevelObject is TCustomForm) then
begin
NewForm := TCustomForm(NewTopLevelObject);
if Screen.GetCurrentModalFormZIndex > Screen.CustomFormZIndex(NewForm) then
begin
// there is a modal form above -> focus forbidden
{$IfDef VerboseFocus}
DebugLn(' there is a modal form above -> focus forbidden');
{$EndIf}
exit;
end;
end;
NewFocusWidget := FindFocusWidget(Widget);
{$IfDef VerboseFocus}
write(' G NewFocusWidget=',DbgS(NewFocusWidget),' ',DbgSName(GetNearestLCLObject(NewFocusWidget)));
write(' WidVisible=',GTK_WIDGET_VISIBLE(PGtkWidget(NewFocusWidget)));
write(' WidRealized=',GTK_WIDGET_REALIZED(PGtkWidget(NewFocusWidget)));
write(' WidMapped=',GTK_WIDGET_MAPPED(PGtkWidget(NewFocusWidget)));
write(' WidCanfocus=',GTK_WIDGET_CAN_FOCUS(PGtkWidget(NewFocusWidget)));
write(' TopLvlVisible=',GTK_WIDGET_VISIBLE(PGtkWidget(TopLevel)));
DebugLn('');
{$EndIf}
if (NewFocusWidget<>nil) and GTK_WIDGET_CAN_FOCUS(NewFocusWidget) then
begin
if (PGtkWindow(TopLevel)^.Focus_Widget<>NewFocusWidget) then
begin
{$IfDef VerboseFocus}
DebugLn(' H SETTING NewFocusWidget=',dbgs(NewFocusWidget),' ',DbgSName(GetNearestLCLObject(NewFocusWidget)));
{$EndIf}
//DebugLn('TGtkWidgetSet.SetFocus TopLevel[',DebugGtkWidgets.GetInfo(TopLevel,false),'] NewFocusWidget=[',DebugGtkWidgets.GetInfo(NewFocusWidget,false),']');
gtk_window_set_focus(PGtkWindow(TopLevel), NewFocusWidget);
{$IfDef VerboseFocus}
DebugLn(' I NewTopLevel FocusWidget=',DbgS(PGtkWindow(TopLevel)^.Focus_Widget),' Success=',dbgs(PGtkWindow(TopLevel)^.Focus_Widget=NewFocusWidget));
{$EndIf}
end;
end;
end
else begin
NewFocusWidget:=Widget;
end;
if (NewFocusWidget <> nil) and not gtk_widget_has_focus(NewFocusWidget) then
begin
// grab the focus to the parent window
NewTopLevelWidget := gtk_widget_get_toplevel(NewFocusWidget);
NewTopLevelObject := GetNearestLCLObject(NewTopLevelWidget);
if (Screen<>nil) and (Screen.GetCurrentModalForm<>nil) and (NewTopLevelObject <>Screen.GetCurrentModalForm) then
begin
{$IFDEF VerboseFocus}
DebugLn('[TGtkWidgetSet.SetFocus] there is a modal form -> not grabbing');
{$ENDIF}
end
else
begin
{$IfDef VerboseFocus}
DebugLn(' J Grabbing focus ',GetWidgetDebugReport(NewFocusWidget));
{$EndIf}
if NewTopLevelObject is TCustomForm then
begin
Info := GetWidgetInfo(NewTopLevelWidget, False);
if (Info <> nil) and not (wwiActivating in Info^.Flags) then
SetForegroundWindow(TCustomForm(NewTopLevelObject).Handle);
end;
gtk_widget_grab_focus(NewFocusWidget);
end;
end;
{$IfDef VerboseFocus}
write('[TGtkWidgetSet.SetFocus] END hWnd=',DbgS(hWnd));
NewFocusWidget:=PGtkWidget(GetFocus);
write(' NewFocus=',DbgS(NewFocusWidget));
AWinControl:=TWinControl(GetNearestLCLObject(NewFocusWidget));
if AWinControl<>nil then
write(' NewLCLParent=',AWinControl.Name,':',AWinControl.ClassName)
else
write(' NewLCLParent=nil');
DebugLn('');
{$EndIf}
end;
{------------------------------------------------------------------------------
Function: SetForegroundWindow
Params: hWnd:
Returns:
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetForegroundWindow(hWnd : HWND): boolean;
var
{$IFDEF VerboseFocus}
LCLObject: TControl;
{$ENDIF}
GdkWindow: PGdkWindow;
AForm: TCustomForm;
{$IFDEF GTK1}
FormWidget: PGtkWidget;
FormWindow: PGdkWindowPrivate;
WindowDesktop: Integer;
{$ENDIF}
begin
{$IFDEF VerboseFocus}
DbgOut('TGtkWidgetSet.SetForegroundWindow hWnd=',DbgS(hWnd));
LCLObject:=TControl(GetLCLObject(Pointer(hWnd)));
if LCLObject<>nil then
DebugLn(' LCLObject=',LCLObject.Name,':',LCLObject.ClassName)
else
DebugLn(' LCLObject=nil');
{$ENDIF}
Result := GtkWidgetIsA(PGtkWidget(hWnd),GTK_TYPE_WINDOW);
if Result then
begin
GdkWindow := GetControlWindow(PgtkWidget(hwnd));
if GdkWindow <> nil then
begin
if not gdk_window_is_visible(GdkWindow) then
begin
Result := False;
Exit;
end;
AForm := TCustomForm(GetLCLObject(PgtkWidget(hwnd)));
if (AForm <> nil) and (AForm is TCustomForm) and (AForm.Parent=nil) then
begin
if Screen.CustomFormZIndex(AForm) < Screen.GetCurrentModalFormZIndex then
begin
debugln('TGtkWidgetSet.SetForegroundWindow Form=',DbgSName(AForm),
' can not be raised, because ',
DbgSName(Screen.GetCurrentModalForm),
' is modal and above.');
Result := False;
exit;
end;
Screen.MoveFormToZFront(AForm);
end;
{$IFDEF DebugGDKTraps}
BeginGDKErrorTrap;
{$ENDIF}
gdk_window_show(GdkWindow);
gdk_window_raise(GdkWindow);
{$IFDEF DebugGDKTraps}
EndGDKErrorTrap;
{$ENDIF}
{$IFDEF GTK1}
FormWidget:=PGtkWidget(AForm.Handle);
FormWindow:=PGdkWindowPrivate(FormWidget^.window);
if FormWindow<>nil then begin
WindowDesktop := GDK_WINDOW_GET_DESKTOP(FormWindow);
// this prevents the window from appearing on a different desktop
// which could be undesirable.
// check if the window is on all desktops or is on the current desktop
if (WindowDesktop < 0) or (WindowDesktop = GDK_GET_CURRENT_DESKTOP) then
begin
GDK_WINDOW_ACTIVATE(FormWindow);
end
else begin
// TODO: Figure out how to set the focus on an inactive desktop without
// bringing the window to the current desktop
end;
end;
{$ELSE}
// this currently will bring the window to the current desktop and focus it
gtk_window_present(PGtkWindow(hWnd));
{$ENDIF}
end;
end;
end;
function TGtkWidgetSet.SetMapMode(DC: HDC; fnMapMode : Integer): Integer;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
Result := Integer(False);
if not IsValidDC(DC) then Exit(0);
DevCtx.MapMode := fnMapMode;
Result := Integer(True);
end;
function TGTKWidgetSet.SetParent(hWndChild: HWND; hWndParent: HWND): HWND;
var
Fixed: PGtkWidget;
LCLObject: TObject;
begin
Result := GetParent(hWndChild);
if Result = hWndParent then
Exit;
// for window we need to move it content to HBox
if GTK_IS_WINDOW(PGtkWidget(hWndChild)) then
begin
LCLObject := GetLCLObject(PGtkWidget(hWndChild));
if LCLObject <> nil then
Controls.RecreateWnd(TWinControl(LCLObject));
Exit;
end;
if Result <> 0 then
begin
// unparent first
gtk_widget_ref(PGtkWidget(hWndChild));
if GTK_IS_CONTAINER(Pointer(Result)) then
gtk_container_remove(PGtkContainer(Result), PGtkWidget(hWndChild))
else
gtk_widget_unparent(PGtkWidget(hWndChild));
end;
Fixed := GetFixedWidget(PGtkWidget(hWndParent));
if Fixed <> nil then
begin
FixedPutControl(Fixed, PGtkWidget(hWndChild), PGtkWidget(hWndChild)^.allocation.x, PGtkWidget(hWndChild)^.allocation.y);
RegroupAccelerator(PGtkWidget(hWndChild));
end
else
gtk_widget_set_parent(PGtkWidget(hWndChild), PGtkWidget(hWndParent));
if Result <> 0 then
gtk_widget_unref(PGtkWidget(hWndChild));
end;
{------------------------------------------------------------------------------
function TGtkWidgetSet.SetProp(Handle: hwnd; Str : PChar;
Data : Pointer) : Boolean;
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetProp(Handle: hwnd; Str : PChar; Data : Pointer) : Boolean;
begin
gtk_object_set_data(pGTKObject(handle),Str,data);
Result:=true;
end;
{------------------------------------------------------------------------------
Method: SetRectRgn
Params: aRGN: HRGN; X1, Y1, X2, Y2 : Integer
Returns: True if the function succeeds
Converts a region into a rectangular region with the specified coordinates.
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetRectRgn(aRGN: HRGN; X1, Y1, X2, Y2 : Integer): Boolean;
procedure Swap(var A, B: Integer);
var
Tmp: Integer;
begin
Tmp := A;
A := B;
B := Tmp;
end;
var
AGdiObject: PGdiObject absolute aRGN;
begin
Result := IsValidGDIObject(aRGN);
if Result then begin
if (X1 > X2) then swap(X1, X2);
if (Y1 > Y2) then swap(Y1, Y2);
AGdiObject^.GDIRegionObject := CreateRectGDKRegion(Rect(X1,Y1,X2,Y2));
Result := True;
end;
end;
{------------------------------------------------------------------------------
function TGtkWidgetSet.SetROPMode(Handle: hwnd; Str : PChar;
Data : Pointer) : Boolean;
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetROP2(DC: HDC; Mode: Integer) : Integer;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
if not IsValidDC(DC) then Exit(0);
Result := DevCtx.ROP2;
DevCtx.ROP2 := Mode;
end;
{------------------------------------------------------------------------------
Function: SetScrollInfo
Params: none
Returns: The new position value
nPage >= 0
nPage <= nMax-nMin+1
nPos >= nMin
nPos <= nMax - Max(nPage-1,0)
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetScrollInfo(Handle : HWND; SBStyle : Integer;
ScrollInfo: TScrollInfo; bRedraw : Boolean): Integer;
procedure SetRangeUpdatePolicy(Range: PGtkRange);
var
UpdPolicy: TGTKUpdateType;
begin
case ScrollInfo.nTrackPos of
SB_POLICY_DISCONTINUOUS: UpdPolicy := GTK_UPDATE_DISCONTINUOUS;
SB_POLICY_DELAYED: UpdPolicy := GTK_UPDATE_DELAYED;
else UpdPolicy := GTK_UPDATE_CONTINUOUS;
end;
gtk_range_set_update_policy(Range, UpdPolicy);
end;
procedure SetScrolledWindowUpdatePolicy(ScrolledWindow:PGTKScrolledWindow);
var
Range: PGtkRange;
begin
case SBStyle of
SB_VERT: Range := PGtkRange(ScrolledWindow^.vscrollbar);
SB_HORZ: Range := PGtkRange(ScrolledWindow^.hscrollbar);
else exit;
end;
SetRangeUpdatePolicy(Range);
end;
const
POLICY: array[BOOLEAN] of TGTKPolicyType = (GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
var
Adjustment: PGtkAdjustment;
Layout: PgtkLayout;
Scroll: PGTKWidget;
IsScrollWindow: Boolean;
IsScrollbarVis: boolean;
begin
Result := 0;
if (Handle = 0) then exit;
{DebugLn(['TGtkWidgetSet.SetScrollInfo A Widget=',GetWidgetDebugReport(PGtkWidget(Handle)),' SBStyle=',SBStyle,
' ScrollInfo=[',
'cbSize=',ScrollInfo.cbSize,
',fMask=',ScrollInfo.fMask,
',nMin=',ScrollInfo.nMin,
',nMax=',ScrollInfo.nMax,
',nPage=',ScrollInfo.nPage,
',nPos=',ScrollInfo.nPos,
',nTrackPos=',ScrollInfo.nTrackPos,
']']);}
Scroll := gtk_object_get_data(PGTKObject(Handle), odnScrollArea);
if GtkWidgetIsA(Scroll, gtk_scrolled_window_get_type)
then begin
IsScrollWindow := True;
end
else begin
Scroll := PGTKWidget(Handle);
IsScrollWindow := GtkWidgetIsA(Scroll, gtk_scrolled_window_get_type);
end;
if IsScrollWindow
then begin
Layout := GetFixedWidget(PGTKObject(Handle));
if not GtkWidgetIsA(PGtkWidget(Layout), gtk_layout_get_type)
then Layout := nil;
end
else begin
Layout := nil;
end;
// scrollbar update policy
if (Scrollinfo.fmask and SIF_UPDATEPOLICY <> 0) then begin
if IsScrollWindow then
SetScrolledWindowUpdatePolicy(PGTKScrolledWindow(Scroll))
else if GtkWidgetIsA(PgtkWidget(Scroll), gtk_clist_get_type) then
SetScrolledWindowUpdatePolicy(PGTKScrolledWindow(@PgtkCList(Scroll)^.container))
else if GtkWidgetIsA(PGtkWidget(Scroll),gtk_hscrollbar_get_type) then
SetRangeUpdatePolicy(PgtkRange(Scroll))
else if GtkWidgetIsA(PGtkWidget(Scroll),gtk_vscrollbar_get_type) then
SetRangeUpdatePolicy(PgtkRange(Scroll))
else if GtkWidgetIsA(PGtkWidget(Scroll), gtk_range_get_type) then
SetRangeUpdatePolicy(PGTKRange(Scroll));
end;
Adjustment:=nil;
case SBStyle of
SB_HORZ:
if IsScrollWindow
then begin
Adjustment := gtk_scrolled_window_get_hadjustment(PGTKScrolledWindow(Scroll));
if Layout <> nil
then begin
if (ScrollInfo.fMask and SIF_RANGE) <> 0
then gtk_layout_set_size(Layout, ScrollInfo.nMax - ScrollInfo.nMin, Layout^.height);
Result := round(Layout^.hadjustment^.value);
end;
end
// obsolete stuff
else if GtkWidgetIsA(PGtkWidget(Scroll),gtk_hscrollbar_get_type)
then begin
// this one shouldn't be possible, scrollbar messages are sent to the CTL
DebugLN('!!! direct SB_HORZ set call to scrollbar');
Adjustment := PgtkhScrollBar(Scroll)^.Scrollbar.Range.Adjustment
end
else if GtkWidgetIsA(PGtkWidget(Scroll),gtk_clist_get_type)
then begin
//clist
//TODO: check if this is needed for listviews
DebugLn('[SetScrollInfo] Possible obsolete set use of CList (Listview ?)');
Adjustment := gtk_clist_get_hadjustment(PgtkCList(Scroll));
end;
SB_VERT:
if IsScrollWindow
then begin
Adjustment := gtk_scrolled_window_get_vadjustment(PGTKScrolledWindow(Scroll));
if Layout <> nil
then begin
if (ScrollInfo.fMask and SIF_RANGE) <> 0
then gtk_layout_set_size(Layout, Layout^.Width, ScrollInfo.nMax - ScrollInfo.nMin);
Result := round(Layout^.vadjustment^.value);
end;
end
// obsolete stuff
else if GtkWidgetIsA(PGtkWidget(Scroll),gtk_vscrollbar_get_type)
then begin
// this one shouldn't be possible, scrollbar messages are sent to the CTL
DebugLN('!!! direct SB_VERT call to scrollbar');
Adjustment := PgtkvScrollBar(Scroll)^.Scrollbar.Range.Adjustment;
end
else if GtkWidgetIsA(PGtkWidget(Scroll), gtk_clist_get_type)
then begin
//TODO: check is this is needed for listviews
DebugLn('[SetScrollInfo] Possible obsolete set use of CList (Listview ?)');
Adjustment := gtk_clist_get_vadjustment(PgtkCList(Scroll));
end;
SB_CTL:
if GtkWidgetIsA(PGtkWidget(Scroll),gtk_vscrollbar_get_type) then
Adjustment := PgtkvScrollBar(Scroll)^.Scrollbar.Range.Adjustment
else
if GtkWidgetIsA(PGtkWidget(Scroll),gtk_hscrollbar_get_type) then
Adjustment := PgtkhScrollBar(Scroll)^.Scrollbar.Range.Adjustment
else
if GtkWidgetIsA(PGtkWidget(Scroll), gtk_range_get_type) then
Adjustment := gtk_range_get_adjustment(PGTKRange(Scroll));
SB_BOTH:
DebugLn('[SetScrollInfo] Got SB_BOTH ???');
end;
if Adjustment = nil then
exit;
if (ScrollInfo.fMask and SIF_RANGE) <> 0
then begin
Adjustment^.lower := ScrollInfo.nMin;
Adjustment^.upper := ScrollInfo.nMax;
end;
if (ScrollInfo.fMask and SIF_PAGE) <> 0
then begin
// 0 <= nPage <= nMax-nMin+1
Adjustment^.page_size := ScrollInfo.nPage;
Adjustment^.page_size := Min(Max(Adjustment^.page_size,0),
Adjustment^.upper-Adjustment^.lower+1);
Adjustment^.page_increment := (Adjustment^.page_size/6)+1;
end;
if (ScrollInfo.fMask and SIF_POS) <> 0
then begin
// nMin <= nPos <= nMax - Max(nPage-1,0)
Adjustment^.value := ScrollInfo.nPos;
Adjustment^.value := Max(Adjustment^.value,Adjustment^.lower);
Adjustment^.value := Min(Adjustment^.value,
Adjustment^.upper-Max(Adjustment^.page_size-1,0));
end;
// check if scrollbar should be hidden
IsScrollbarVis := true;
if ((ScrollInfo.fMask and (SIF_RANGE or SIF_PAGE)) <> 0) and
((SBStyle=SB_HORZ) or (SBStyle=SB_VERT))
then begin
if (Adjustment^.lower >= (Adjustment^.upper-Max(adjustment^.page_size-1,0)))
then begin
if (ScrollInfo.fMask and SIF_DISABLENOSCROLL) = 0 then
IsScrollbarVis := false
else
;// scrollbar should look disabled (no thumbbar and grayed appearance)
// maybe not possible in gtk
end;
end;
Result := Round(Adjustment^.value);
{DebugLn('');
DebugLn('[TGtkWidgetSet.SetScrollInfo] Result=',Result,
' Lower=',RoundToInt(Lower),
' Upper=',RoundToInt(Upper),
' Page_Size=',RoundToInt(Page_Size),
' Page_Increment=',RoundToInt(Page_Increment),
' bRedraw=',bRedraw,
' Handle=',DbgS(Handle));}
// do we have to set this always ?
// ??? what is this for code ????
// why not change adjustment if we don't do a redraw ???
if bRedraw then
begin
if IsScrollWindow
then begin
case SBStyle of
SB_HORZ:
gtk_object_set(PGTKObject(Scroll),'hscrollbar_policy',[POLICY[IsScrollbarVis],nil]);
SB_VERT:
gtk_object_set(PGTKObject(Scroll),'vscrollbar_policy',[POLICY[IsScrollbarVis],nil]);
end;
end
else
gtk_widget_queue_draw(PGTKWidget(Scroll));
(*
DebugLn('TGtkWidgetSet.SetScrollInfo:' +
' lower=%d/%d upper=%d/%d value=%d/%d' +
' step_increment=%d/1 page_increment=%d/%d page_size=%d/%d', [
Round(lower),nMin, Round(upper),nMax, Round(value),nPos,
Round(step_increment), Round(page_increment),nPage, Round(page_size),nPage]
);
*)
gtk_adjustment_changed(Adjustment);
end;
end;
{------------------------------------------------------------------------------
Function: SetSysColors
Params: cElements: the number of elements
lpaElements: array with element numbers
lpaRgbValues: array with colors
Returns: 0 if unsuccesful
The SetSysColors function sets the colors for one or more display elements.
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetSysColors(cElements: Integer; const lpaElements;
const lpaRgbValues): Boolean;
var
n: Integer;
Element: LongInt;
begin
Result := False;
if cElements > MAX_SYS_COLORS then Exit;
for n := 0 to cElements - 1 do
begin
Element := PInteger(lpaElements)[n];
if (Element > MAX_SYS_COLORS) or (Element < 0) then
Exit;
SysColorMap[Element] := PDword(@lpaRgbValues)[n];
//DebugLn(Format('Trace:[TGtkWidgetSet.SetSysColor] Index %d (%8x) --> %8x', [PLongArray(lpaElements)^[n], SysColorMap[PLongArray(lpaElements)^[n]], PLongArray(lpaRgbValues)^[n]]));
end;
//TODO send WM_SYSCOLORCHANGE
Result := True;
end;
{------------------------------------------------------------------------------
Function: SetTextCharacterExtra
Params: _hdc:
nCharExtra:
Returns:
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetTextCharacterExtra(DC : hdc; nCharExtra : Integer):Integer;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
// Your code here
Result:=0;
end;
{------------------------------------------------------------------------------
Function: SetTextColor
Params: hdc: Identifies the device context.
Color: Specifies the color of the text.
Returns: The previous color if succesful, CLR_INVALID otherwise
The SetTextColor function sets the text color for the specified device
context to the specified color.
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetTextColor(DC: HDC; Color: TColorRef): TColorRef;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
//DebugLn(Format('trace:> [TGtkWidgetSet.SetTextColor] DC: 0x%x Color: %8x', [Integer(DC), Color]));
Result := CLR_INVALID;
if IsValidDC(DC)
then begin
with TGtkDeviceContext(DC) do
begin
Result := CurrentTextColor.ColorRef;
SetGDIColorRef(CurrentTextColor,Color);
if Result<>Color then
SelectedColors := dcscCustom; // force SelectGDKTextProps to ensure text color
end;
end;
//DebugLn(Format('trace:< [TGtkWidgetSet.SetTextColor] DC: 0x%x Color: %8x --> %8x', [Integer(DC), Color, Result]));
end;
function TGtkWidgetSet.SetViewPortExtEx(DC: HDC; XExtent, YExtent : Integer; OldSize: PSize): Boolean;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
Result := False;
if not IsValidDC(DC) then Exit;
if OldSize <> nil then
begin
OldSize^.cx := DevCtx.ViewPortExt.x;
OldSize^.cy := DevCtx.ViewPortExt.y;
end;
if (XExtent <> DevCtx.ViewPortExt.x) or (YExtent <> DevCtx.ViewPortExt.y) then
begin
case DevCtx.MapMode of
MM_ANISOTROPIC, MM_ISOTROPIC:
begin
DevCtx.ViewPortExt := Point(XExtent, YExtent);
Result := True;
end;
end;
end;
end;
function TGtkWidgetSet.SetViewPortOrgEx(DC: HDC; NewX, NewY: Integer; OldPoint: PPoint): Boolean;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
Result := False;
if not IsValidDC(DC) then Exit;
if OldPoint <> nil then
begin
OldPoint^.x := DevCtx.ViewPortOrg.x;
OldPoint^.y := DevCtx.ViewPortOrg.y;
end;
if (NewX <> DevCtx.ViewPortOrg.x) or (NewY <> DevCtx.ViewPortOrg.y) then
begin
DevCtx.ViewPortOrg := Point(NewX, NewY);
Result := True;
end;
end;
function TGtkWidgetSet.SetWindowExtEx(DC: HDC; XExtent, YExtent: Integer; OldSize: PSize): Boolean;
var
DevCtx: TGtkDeviceContext absolute DC;
begin
Result := False;
if not IsValidDC(DC) then Exit;
if OldSize <> nil then
begin
OldSize^.cx := DevCtx.WindowExt.x;
OldSize^.cy := DevCtx.WindowExt.y;
end;
if (XExtent <> DevCtx.WindowExt.x) or (YExtent <> DevCtx.WindowExt.y) then
begin
case DevCtx.MapMode of
MM_ANISOTROPIC, MM_ISOTROPIC:
begin
DevCtx.WindowExt := Point(XExtent, YExtent);
Result := True;
end;
end;
end;
end;
{------------------------------------------------------------------------------
Procedure: SetWindowLong
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetWindowLong(Handle: HWND; Idx: Integer;
NewLong: PtrInt): PtrInt;
var
Data: Pointer;
WidgetInfo: PWidgetInfo;
begin
//TODO: Finish this;
//DebugLn(Format('Trace:> [TGtkWidgetSet.SETWINDOWLONG] HWND: 0x%x, idx: 0x%x(%d), Value: 0x%x(%d)', [Handle, idx, idx, newlong, newlong]));
Result:=0;
Data := Pointer(NewLong);
case idx of
GWL_WNDPROC :
begin
WidgetInfo := GetWidgetInfo(Pointer(Handle));
if WidgetInfo <> nil then
WidgetInfo^.WndProc := NewLong;
end;
GWL_HINSTANCE :
begin
gtk_object_set_data(pgtkobject(Handle),'HINSTANCE',Data);
end;
GWL_HWNDPARENT :
begin
gtk_object_set_data(pgtkobject(Handle),'HWNDPARENT',Data);
end;
GWL_STYLE :
begin
WidgetInfo := GetWidgetInfo(Pointer(Handle));
if WidgetInfo <> nil then
WidgetInfo^.Style := NewLong;
end;
GWL_EXSTYLE :
begin
WidgetInfo := GetWidgetInfo(Pointer(Handle));
if WidgetInfo <> nil then
WidgetInfo^.ExStyle := NewLong;
end;
GWL_USERDATA :
begin
gtk_object_set_data(pgtkobject(Handle),'Userdata',Data);
end;
GWL_ID :
begin
gtk_object_set_data(pgtkobject(Handle),'ID',Data);
end;
end; //case
//DebugLn(Format('Trace:< [TGtkWidgetSet.SETWINDOWLONG] HWND: 0x%x, idx: 0x%x(%d), Value: 0x%x(%d) --> 0x%x(%d)', [Handle, idx, idx, newlong, newlong, Result, Result]));
end;
{------------------------------------------------------------------------------
function TGtkWidgetSet.SetWindowOrgEx(DC : HDC; NewX, NewY : Integer;
OldPoint: PPoint) : Boolean;
Sets the DC offset for the specified device context.
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetWindowOrgEx(DC : HDC; NewX, NewY : Integer;
OldPoint: PPoint) : Boolean;
var
DevCtx: TGtkDeviceContext absolute DC;
OldP: TPoint;
begin
//DebugLn('[TGtkWidgetSet.SetWindowOrgEx] ',NewX,' ',NewY);
GetWindowOrgEx(DC, @OldP);
Result := MoveWindowOrgEx(DC, -NewX - OldP.X, -NewY - OldP.Y);
if OldPoint <> nil then
OldPoint^ := OldP;
end;
{------------------------------------------------------------------------------
function TGtkWidgetSet.SetWindowPos(hWnd: HWND; hWndInsertAfter: HWND;
X, Y, cx, cy: Integer; uFlags: UINT): Boolean;
hWnd: Widget to move
hWndInsertAfter:
HWND_BOTTOM to move bottommost
HWND_TOP to move topmost
the Widget, that should lie just on top of hWnd
uFlags:
SWP_NOMOVE: ignore X, Y
SWP_NOSIZE: ignore cx, cy
SWP_NOZORDER: ignore hWndInsertAfter
SWP_NOREDRAW: skip instant redraw
SWP_NOACTIVATE: skip switching focus
------------------------------------------------------------------------------}
function TGtkWidgetSet.SetWindowPos(hWnd: HWND; hWndInsertAfter: HWND;
X, Y, cx, cy: Integer; uFlags: UINT): Boolean;
procedure SetZOrderOnFixedWidget(Widget, FixedWidget: PGtkWidget);
var
OldListItem: PGList;
AfterWidget: PGtkWidget;
AfterListItem: PGList;
begin
OldListItem:=FindFixedChildListItem(PGtkFixed(FixedWidget),Widget);
if OldListItem=nil then begin
DebugLn('TGtkWidgetSet.SetWindowPos.SetZOrderOnFixedWidget WARNING: Widget not on parents fixed widget');
exit;
end;
AfterWidget:=nil;
AfterListItem:=nil;
if hWndInsertAfter=HWND_BOTTOM then begin
//debugln('HWND_BOTTOM');
// HWND_BOTTOM
end else if hWndInsertAfter=HWND_TOP then begin
//debugln('HWND_TOP');
// HWND_TOP
AfterListItem:=FindFixedLastChildListItem(PGtkFixed(FixedWidget));
end else if hWndInsertAfter=0 then begin
DebugLn('TGtkWidgetSet.SetWindowPos.SetZOrderOnFixedWidget WARNING: hWndInsertAfter=0');
exit;
end else begin
// hWndInsertAfter
AfterWidget:=PGtkWidget(hWndInsertAfter);
AfterListItem:=FindFixedChildListItem(PGtkFixed(FixedWidget),AfterWidget);
//debugln('AfterWidget=',GetWidgetDebugReport(AfterWidget));
end;
if (AfterListItem=nil) and (AfterWidget<>nil) then begin
DebugLn('TGtkWidgetSet.SetWindowPos.SetZOrderOnFixedWidget WARNING: AfterWidget not on parents fixed widget');
exit;
end;
if (OldListItem=AfterListItem) or (OldListItem^.next=AfterListItem) then
begin
{$IFDEF EnableGtkZReordering}
DebugLn('TGtkWidgetSet.SetWindowPos.SetZOrderOnFixedWidget Hint: Already there');
{$ENDIF}
exit;
end;
//DebugLn('TGtkWidgetSet.SetWindowPos Moving GList entry');
// reorder
{$IFDEF EnableGtkZReordering}
// MG: This trick does not work properly
debugln('SetZOrderOnFixedWidget FixedWidget=['+GetWidgetDebugReport(FixedWidget)+']',
' Widget=['+GetWidgetDebugReport(Widget)+']',
' AfterWidget=['+GetWidgetDebugReport(AfterWidget)+']');
MoveGListLinkBehind(PGtkFixed(FixedWidget)^.children,
OldListItem,AfterListItem);
if GTK_WIDGET_VISIBLE(FixedWidget) and GTK_WIDGET_VISIBLE(Widget)
and GTK_WIDGET_MAPPED(Widget) then begin
DebugLn('TGtkWidgetSet.SetWindowPos.SetZOrderOnFixedWidget resize ..');
gtk_widget_queue_resize(FixedWidget);
AfterListItem:=PGtkFixed(FixedWidget)^.children;
while AfterListItem<>nil do begin
AfterWidget:=GetFixedChildListWidget(AfterListItem);
DebugLn('TGtkWidgetSet.SetWindowPos.SetZOrderOnFixedWidget A ',GetWidgetDebugReport(AfterWidget));
AfterListItem:=AfterListItem^.next;
end;
end;
{$ENDIF}
end;
procedure SetZOrderOnLayoutWidget(Widget, LayoutWidget: PGtkWidget);
begin
//DebugLn('TGtkWidgetSet.SetWindowPos.SetZOrderOnLayoutWidget Not implemented: ZOrdering .. on ',GetWidgetDebugReport(LayoutWidget));
end;
var
Widget: PGTKWidget;
FixedWidget: PGtkWidget;
begin
Result:=false;
Widget:=PGtkWidget(hWnd);
{DebugLn('[TGtkWidgetSet.SetWindowPos] ',GetWidgetDebugReport(Widget),
' Top=',hWndInsertAfter=HWND_TOP,
' SWP_NOZORDER=',(SWP_NOZORDER and uFlags)<>0,
' SWP_NOSIZE=',(SWP_NOSIZE and uFlags)<>0,
' SWP_NOMOVE=',(SWP_NOMOVE and uFlags)<>0,
'');}
if GtkWidgetIsA(Widget,GTK_TYPE_WINDOW) then begin
{ case hWndInsertAfter of
HWND_BOTTOM: ; //gdk_window_lower(Widget^.Window);
HWND_TOP: gtk_window_set_position(PGtkWindow(hWnd),GTK_WIN_POS_CENTER);
//gdk_window_raise(Widget^.Window);
end;
}
end else if (SWP_NOZORDER and uFlags)=0 then begin
FixedWidget:=Widget^.Parent;
if FixedWidget=nil then exit;
//DebugLn('TGtkWidgetSet.SetWindowPos ZOrdering .. on ',GetWidgetDebugReport(FixedWidget));
if GtkWidgetIsA(FixedWidget,GTK_Fixed_Get_Type) then begin
// parent's client area is a gtk_fixed widget
SetZOrderOnFixedWidget(Widget,FixedWidget);
end else if GtkWidgetIsA(FixedWidget,GTK_Layout_Get_Type) then begin
// parent's client area is a gtk_layout widget
SetZOrderOnLayoutWidget(Widget,FixedWidget);
end else begin
//DebugLn('TGtkWidgetSet.SetWindowPos Not implemented: ZOrdering .. on ',GetWidgetDebugReport(FixedWidget));
exit;
end;
end;
Result:=true;
end;
{------------------------------------------------------------------------------
Function: ShowCaret
Params: none
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.ShowCaret(hWnd: HWND): Boolean;
var
GTKObject: PGTKObject;
begin
//DebugLn(Format('Trace:> [TGtkWidgetSet.ShowCaret] HWND: 0x%x', [hWnd]));
GTKObject := PGTKObject(HWND);
Result := GTKObject <> nil;
if Result
then begin
if gtk_type_is_a(gtk_object_type(GTKObject), GTKAPIWidget_GetType)
then begin
GTKAPIWidget_ShowCaret(PGTKAPIWidget(GTKObject));
end
else begin
Result := False;
end;
end
else DebugLn('WARNING: [TGtkWidgetSet.ShowCaret] Got null HWND');
//DebugLn(Format('Trace:< [TGtkWidgetSet.ShowCaret] HWND: 0x%x --> %s', [hWnd, BOOL_TEXT[Result]]));
end;
{------------------------------------------------------------------------------
Function: ShowScrollBar
Params: Wnd, wBar, bShow
Returns: Nothing
------------------------------------------------------------------------------}
function TGtkWidgetSet.ShowScrollBar(Handle: HWND; wBar: Integer;
bShow: Boolean): Boolean;
var
NewPolicy: Integer;
Scroll: PGtkWidget;
IsScrollWindow: Boolean;
begin
//DebugLn('trace:[TGtkWidgetSet.ShowScrollBar]');
Result := (Handle <> 0);
if not Result then exit;
Scroll := PGtkWidget(gtk_object_get_data(PGTKObject(Handle), odnScrollArea));
if GtkWidgetIsA(Scroll, gtk_scrolled_window_get_type)
then begin
IsScrollWindow := True;
end
else begin
Scroll := PGTKWidget(Handle);
IsScrollWindow := GtkWidgetIsA(Scroll, gtk_scrolled_window_get_type);
end;
//DebugLn(['TGtkWidgetSet.ShowScrollBar ',GetWidgetDebugReport(Scroll),' wBar=',wBar,' bShow=',bShow]);
if IsScrollWindow then begin
if wBar in [SB_BOTH, SB_HORZ] then begin
//DebugLn(['TGtkWidgetSet.ShowScrollBar ',GetWidgetDebugReport(Widget),' bShow=',bShow]);
if bShow then
NewPolicy:=GTK_POLICY_ALWAYS
else
NewPolicy:=GTK_POLICY_NEVER;
gtk_object_set(PGTKObject(Scroll), 'hscrollbar_policy', [NewPolicy,nil]);
end;
if wBar in [SB_BOTH, SB_VERT] then begin
if bShow then
NewPolicy:=GTK_POLICY_ALWAYS
else
NewPolicy:=GTK_POLICY_NEVER;
gtk_object_set(PGTKObject(Scroll), 'vscrollbar_policy', [NewPolicy,nil]);
end;
end
else begin
if (wBar = SB_CTL)
and gtk_type_is_a(gtk_object_type(PGTKObject(Handle)),gtk_widget_get_type)
then begin
if bShow
then gtk_widget_show(Scroll)
else gtk_widget_hide(Scroll);
end;
end;
end;
{------------------------------------------------------------------------------
function ShowWindow(hWnd: HWND; nCmdShow: Integer): Boolean;
nCmdShow:
SW_SHOWNORMAL, SW_MINIMIZE, SW_SHOWMAXIMIZED
------------------------------------------------------------------------------}
function TGtkWidgetSet.ShowWindow(hWnd: HWND; nCmdShow: Integer): Boolean;
var
GtkWindow: PGtkWindow;
Widget: PGtkWidget;
begin
Result:=false;
Widget := PGtkWidget(hWND);
if Widget = nil then
RaiseGDBException('TGtkWidgetSet.ShowWindow hWnd is nil');
if not GtkWidgetIsA(Widget,GTK_TYPE_WINDOW) then
begin
// we are pure gtkwidget so only SW_SHOW AND SW_HIDE CAN GO
case nCmdShow of
SW_SHOWNORMAL,
SW_SHOW: gtk_widget_show(Widget);
SW_HIDE: gtk_widget_hide(Widget);
end;
Result := nCmdShow in [SW_SHOW, SW_HIDE];
exit;
end;
GtkWindow:=PGtkWindow(hWnd);
if GtkWindow=nil then
RaiseGDBException('TGtkWidgetSet.ShowWindow hWnd is nil');
if not GtkWidgetIsA(PGtkWidget(GtkWindow),GTK_TYPE_WINDOW) then
RaiseGDBException('TGtkWidgetSet.ShowWindow hWnd is not a gtkwindow');
{$IFDEF Gtk2}
// Implemented on gtk2winapi.inc
// This ifdef is necessary otherwise the gtk2 interface wont compile
{$ELSE}
case nCmdShow of
SW_SHOWNORMAL:
begin
{$IFDEF DebugGDK}BeginGDKErrorTrap;{$ENDIF}
gdk_window_show(PgtkWidget(GtkWindow)^.Window);
{$IFDEF DebugGDKTraps}EndGDKErrorTrap;{$ENDIF}
end;
SW_HIDE:
begin
gdk_window_hide(PgtkWidget(GtkWindow)^.Window);
end;
SW_MINIMIZE:
begin
GDK_WINDOW_MINIMIZE(PGdkWindowPrivate(PgtkWidget(GtkWindow)^.Window));
end;
SW_SHOWMAXIMIZED:
begin
GDK_WINDOW_MAXIMIZE(PGdkWindowPrivate(PgtkWidget(GtkWindow)^.Window));
end;
end;
{$ENDIF}
Result:=true;
end;
{------------------------------------------------------------------------------
Function: StretchBlt
Params: DestDC: The destination devicecontext
X, Y: The left/top corner of the destination rectangle
Width, Height: The size of the destination rectangle
SrcDC: The source devicecontext
XSrc, YSrc: The left/top corner of the source rectangle
SrcWidth, SrcHeight: The size of the source rectangle
ROp: The raster operation to be performed
Returns: True if succesful
The StretchBlt function copies a bitmap from a source rectangle into a
destination rectangle using the specified raster operation. If needed it
resizes the bitmap to fit the dimensions of the destination rectangle.
Sizing is done according to the stretching mode currently set in the
destination device context.
If SrcDC contains a mask the pixmap will be copied with this transparency.
ToDo: Mirroring, extended NonDrawable support (Image, Bitmap, etc)
------------------------------------------------------------------------------}
function TGtkWidgetSet.StretchBlt(DestDC: HDC; X, Y, Width, Height: Integer;
SrcDC: HDC; XSrc, YSrc, SrcWidth, SrcHeight: Integer; ROp: Cardinal): Boolean;
begin
Result:=StretchCopyArea(DestDC,X,Y,Width,Height,
SrcDC,XSrc,YSrc,SrcWidth,SrcHeight,
0,0,0,
ROp);
end;
{------------------------------------------------------------------------------
Function: StretchMaskBlt
Params: DestDC: The destination devicecontext
X, Y: The left/top corner of the destination rectangle
Width, Height: The size of the destination rectangle
SrcDC: The source devicecontext
XSrc, YSrc: The left/top corner of the source rectangle
SrcWidth, SrcHeight: The size of the source rectangle
Mask: The handle of a monochrome bitmap
XMask, YMask: The left/top corner of the mask rectangle
ROp: The raster operation to be performed
Returns: True if succesful
The StretchMaskBlt function copies a bitmap from a source rectangle into a
destination rectangle using the specified mask and raster operation. If needed
it resizes the bitmap to fit the dimensions of the destination rectangle.
Sizing is done according to the stretching mode currently set in the
destination device context.
------------------------------------------------------------------------------}
function TGtkWidgetSet.StretchMaskBlt(DestDC: HDC; X, Y, Width, Height: Integer;
SrcDC: HDC; XSrc, YSrc, SrcWidth, SrcHeight: Integer; Mask: HBITMAP;
XMask, YMask: Integer; Rop: DWORD): Boolean;
begin
Result:=StretchCopyArea(DestDC,X,Y,Width,Height,
SrcDC,XSrc,YSrc,SrcWidth,SrcHeight,
Mask,XMask,YMask,
Rop);
end;
function TGTKWidgetSet.SystemParametersInfo(uiAction: DWord; uiParam: DWord;
pvParam: Pointer; fWinIni: DWord): LongBool;
begin
Result:=False;
Case uiAction of
SPI_GETWORKAREA: begin
TRect(pvParam^):=Bounds(GetSystemMetrics(SM_XVIRTUALSCREEN),
GetSystemMetrics(SM_YVIRTUALSCREEN),
GetSystemMetrics(SM_CXVIRTUALSCREEN),
GetSystemMetrics(SM_CYVIRTUALSCREEN));
Result:=True;
end;
end;
end;
{------------------------------------------------------------------------------
Function: TextOut
Params: DC:
X:
Y:
Str:
Count:
Returns:
------------------------------------------------------------------------------}
function TGtkWidgetSet.TextOut(DC: HDC; X,Y : Integer; Str : Pchar;
Count: Integer) : Boolean;
{$IfDef GTK2}
begin
DebugLn('TGtkWidgetSet.TextOut ToDo');
Result:=false;
end;
{$ELSE}
var
DevCtx: TGtkDeviceContext absolute DC;
aRect : TRect;
txtpt : TPoint;
sz : TSize;
UseFont : PGDKFont;
Underline,
StrikeOut : Boolean;
DCOrigin: TPoint;
TempPen : hPen;
LogP : TLogPen;
Points : array[0..1] of TSize;
lbearing, rbearing, width, ascent,descent: LongInt;
begin
if not IsValidDC(DC) then Exit(False);
if Count <= 0 then Exit(True);
UseFont := GetGtkFont(DevCtx);
if (DevCtx.CurrentFont = nil) or (DevCtx.CurrentFont^.GDIFontObject = nil)
then begin
Underline := False;
StrikeOut := False;
end
else begin
Underline := DevCtx.CurrentFont^.LogFont.lfUnderline <> 0;
StrikeOut := DevCtx.CurrentFont^.LogFont.lfStrikeOut <> 0;
end;
if DevCtx.HasTransf then
DevCtx.TransfPoint(X, Y);
DCOrigin := DevCtx.Offset;
descent:=0;
gdk_text_extents(UseFont, Str, Count, @lbearing, @rBearing, @width, @ascent, @descent);
sz.cx := width;
Sz.cY := ascent+descent;
aRect := Rect(X+DCOrigin.X,Y+DCOrigin.Y,X + Sz.CX, Sz.CY);
FillRect(DC, aRect, hBrush(PtrUInt(DevCtx.GetBrush)));
UpdateDCTextMetric(DevCtx);
TxtPt.X := X;
TxtPt.Y := Y + DevCtx.DCTextMetric.TextMetric.tmAscent;
DevCtx.SelectTextProps;
{$IFDEF DebugGDK}BeginGDKErrorTrap;{$ENDIF}
gdk_draw_text(DevCtx.Drawable, UseFont, DevCtx.GC, TxtPt.X+DCOrigin.X, TxtPt.Y+DCOrigin.Y, Str, Count);
{$IFDEF DebugGDKTraps}EndGDKErrorTrap;{$ENDIF}
if not(Underline or StrikeOut) then Exit(True);
{Create & select pen of font color}
LogP.lopnStyle := PS_SOLID;
LogP.lopnWidth.X := 1;
LogP.lopnColor := GetTextColor(DC);
TempPen := SelectObject(DC, CreatePenIndirect(LogP));
{Get line(s) horizontal position(s)}
Points[0].cX := X;
Points[1].cX := X + sz.cX;
{Draw line(s)}
if Underline
then begin
with DevCtx.DCTextMetric.TextMetric do
Points[0].cY := Y + 2 + tmHeight - tmDescent;
Points[1].cY := Points[0].cY;
Polyline(DC, PPoint(@Points[0]), 2);
end;
if StrikeOut
then begin
Points[0].cY := Y + 2 + (TxtPt.Y - Y) div 2;
Points[1].cY := Points[0].cY;
Polyline(DC, PPoint(@Points[0]), 2);
end;
DeleteObject(SelectObject(DC, TempPen));
Result := True;
end;
{$EndIf}
{------------------------------------------------------------------------------
Function: WindowFromPoint
Params: Point: Specifies the x and y Coords
Returns: The handle of the gtkwidget. If none exist, then NULL is returned.
------------------------------------------------------------------------------}
function TGtkWidgetSet.WindowFromPoint(APoint: TPoint): HWND;
var
ev: TgdkEvent;
Window: PgdkWindow;
Widget: PgtkWidget;
p: TPoint;
begin
// return cached value to prevent heavy gdk_window_at_pointer call
if (APoint = LastWFPMousePos) and GTK_IS_OBJECT(Pointer(LastWFPResult)) then
Exit(LastWFPResult);
Result := 0;
// !!!gdk_window_at_pointer changes the coordinates!!!
// -> using local variable p
p := APoint;
Window := gdk_window_at_pointer(@p.x, @p.Y);
if window <> nil then
begin
FillChar(ev, SizeOf(ev), 0);
ev.any.window := Window;
Widget := gtk_get_event_widget(@ev);
Result := PtrUInt(Widget);
end;
// disconnect old handler
if GTK_IS_OBJECT(Pointer(LastWFPResult)) then
begin
{$IFDEF gtk1}
gtk_signal_disconnect_by_func(GPointer(LastWFPResult),
TGTKSignalFunc(@DestroyWindowFromPointCB), nil);
{$ELSE}
g_signal_handlers_disconnect_by_func(GPointer(LastWFPResult),
TGTKSignalFunc(@DestroyWindowFromPointCB), nil);
{$ENDIF}
end;
LastWFPMousePos := APoint;
LastWFPResult := Result;
// connect handler
if LastWFPResult <> 0 then
{$IFDEF gtk1}
gtk_signal_connect(PGtkObject(LastWFPResult), 'destroy',
TGTKSignalFunc(@DestroyWindowFromPointCB), nil);
{$else}
g_signal_connect(GPointer(LastWFPResult), 'destroy',
TGTKSignalFunc(@DestroyWindowFromPointCB), nil);
{$endif}
end;
//##apiwiz##eps## // Do not remove
// Placed CriticalSectionSupport outside the API wizard bounds
// so it won't affect sorting etc.
{$IfNDef DisableCriticalSections}
{$IfDef Unix}
{$Define pthread}
{Type
_pthread_fastlock = packed record
__status: Longint;
__spinlock: Integer;
end;
pthread_mutex_t = packed record
__m_reserved: Integer;
__m_count: Integer;
__m_owner: Pointer;
__m_kind: Integer;
__m_lock: _pthread_fastlock;
end;
ppthread_mutex_t = ^pthread_mutex_t;
pthread_mutexattr_t = packed record
__mutexkind: Integer;
end;}
{$linklib pthread}
{function pthread_mutex_init(var Mutex: pthread_mutex_t;
var Attr: pthread_mutexattr_t): Integer; cdecl;external;
function pthread_mutexattr_settype(var Attr: pthread_mutexattr_t;
Kind: Integer): Integer; cdecl;external;
function pthread_mutex_lock(var Mutex: pthread_mutex_t):
Integer; cdecl; external;
function pthread_mutex_unlock(var Mutex: pthread_mutex_t):
Integer; cdecl; external;
function pthread_mutex_destroy(var Mutex: pthread_mutex_t):
Integer; cdecl; external;}
{$EndIf}
{$EndIf}
procedure TGtkWidgetSet.InitializeCriticalSection(var CritSection: TCriticalSection);
{$IfDef pthread}
var
ACritSec: System.PRTLCriticalSection;
begin
New(ACritSec);
System.InitCriticalSection(ACritSec^);
CritSection:=TCriticalSection(ACritSec);
end;
{var
Crit : ppthread_mutex_t;
Attribute: pthread_mutexattr_t;
begin
if pthread_mutexattr_settype(Attribute, 1) <> 0 then
Exit;
If CritSection <> 0 then
Try
Crit := ppthread_mutex_t(CritSection);
Dispose(Crit);
except
CritSection := 0;
end;
New(Crit);
pthread_mutex_init(Crit^, Attribute);
CritSection := Longint(Crit);
end;}
{$Else}
begin
end;
{$EndIf}
procedure TGtkWidgetSet.EnterCriticalSection(var CritSection: TCriticalSection);
{$IfDef pthread}
var
ACritSec: System.PRTLCriticalSection;
begin
ACritSec:=System.PRTLCriticalSection(CritSection);
System.EnterCriticalsection(ACritSec^);
end;
{var
Crit,
tmp : ppthread_mutex_t;
begin
New(Crit);
If CritSection <> 0 then
Try
Crit^ := ppthread_mutex_t(CritSection)^;
except
begin
CritSection := Longint(Crit);
exit;
end;
end;
pthread_mutex_lock(Crit^);
tmp := ppthread_mutex_t(CritSection);
CritSection := Longint(Crit);
Dispose(Tmp);
end;}
{$Else}
begin
end;
{$EndIf}
procedure TGtkWidgetSet.LeaveCriticalSection(var CritSection: TCriticalSection);
{$IfDef pthread}
var
ACritSec: System.PRTLCriticalSection;
begin
ACritSec:=System.PRTLCriticalSection(CritSection);
System.LeaveCriticalsection(ACritSec^);
end;
{var
Crit,
tmp : ppthread_mutex_t;
begin
New(Crit);
If CritSection <> 0 then
Try
Crit^ := ppthread_mutex_t(CritSection)^;
except
begin
CritSection := Longint(Crit);
exit;
end;
end;
pthread_mutex_unlock(Crit^);
tmp := ppthread_mutex_t(CritSection);
CritSection := Longint(Crit);
Dispose(Tmp);
end;}
{$Else}
begin
end;
{$EndIf}
procedure TGtkWidgetSet.DeleteCriticalSection(var CritSection: TCriticalSection);
{$IfDef pthread}
var
ACritSec: System.PRTLCriticalSection;
begin
ACritSec:=System.PRTLCriticalSection(CritSection);
System.DoneCriticalsection(ACritSec^);
Dispose(ACritSec);
CritSection:=0;
end;
{var
Crit,
tmp : ppthread_mutex_t;
begin
New(Crit);
If CritSection <> 0 then
Try
Crit^ := ppthread_mutex_t(CritSection)^;
except
begin
CritSection := Longint(Crit);
exit;
end;
end;
pthread_mutex_destroy(Crit^);
Dispose(Crit);
tmp := ppthread_mutex_t(CritSection);
CritSection := 0;
Dispose(Tmp);
end;}
{$Else}
begin
end;
{$EndIf}
{$IfDef ASSERT_IS_ON}
{$UNDEF ASSERT_IS_ON}
{$C-}
{$EndIf}