summaryrefslogtreecommitdiff
path: root/gfx/gdi/gfx_gdi.pas
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/gdi/gfx_gdi.pas')
-rw-r--r--gfx/gdi/gfx_gdi.pas1730
1 files changed, 1730 insertions, 0 deletions
diff --git a/gfx/gdi/gfx_gdi.pas b/gfx/gdi/gfx_gdi.pas
new file mode 100644
index 00000000..10945b54
--- /dev/null
+++ b/gfx/gdi/gfx_gdi.pas
@@ -0,0 +1,1730 @@
+{
+ fpGUI - Free Pascal GUI Library
+
+ GFX_GDI - Windows GDI specific code
+
+ Copyright (C) 2000 - 2006 See the file AUTHORS, included in this
+ distribution, for details of the copyright.
+
+ See the file COPYING.modifiedLGPL, included in this distribution,
+ for details about redistributing fpGUI.
+
+ 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.
+}
+unit GFX_GDI;
+
+{$ifdef fpc}
+ {$mode delphi}{$H+}
+{$endif}
+
+interface
+
+uses
+ Windows,
+ SysUtils, Classes,
+ GfxBase;
+
+
+resourcestring
+ SGDICanvasInvalidFontClass =
+ 'Tried to set font of class "%s" into GDI context. '
+ + 'Only TGDIFont is allowed.';
+
+{ Constants missing on windows unit }
+const
+ WM_MOUSEWHEEL = $020a;
+
+ VER_PLATFORM_WIN32_CE = 3;
+
+{ Unicode selection variables }
+var
+ UnicodeEnabledOS: Boolean;
+
+ WinVersion: TOSVersionInfo;
+
+type
+
+ EGDIError = class(EGfxError);
+
+ { TGDIFont }
+
+ TGDIFont = class(TFCustomFont)
+ public
+ class function GetDefaultFontName(const AFontClass: TGfxFontClass): String; override;
+ constructor Create(const Descriptor: String);
+ destructor Destroy; override;
+ end;
+
+
+ { TGDICanvas }
+
+ PGDICanvasState = ^TGDICanvasState;
+ TGDICanvasState = record
+ Prev: PGDICanvasState;
+ Matrix: TGfxMatrix;
+ Color, PenColor, FontColor: TGfxPixel;
+ PenLineStyle: TGfxLineStyle;
+ Font: TFCustomFont;
+ CurFontHandle: HFONT;
+ end;
+
+ TGDICanvas = class(TFCustomCanvas)
+ private
+ FHandle: HDC;
+ FColor, FBrushColor, FPenColor, FFontColor: TGfxPixel;
+ FLineStyle, FPenLineStyle: TGfxLineStyle;
+ FBrush, FOldBrush: HBRUSH;
+ FPen, FOldPen: HPEN;
+ FFont: TFCustomFont;
+ FFontHandle, FDefaultFontHandle, FCurFontHandle: HFONT;
+ FFontMetrics: TTextMetric;
+ FStateStackpointer: PGDICanvasState;
+ procedure Resized(NewWidth, NewHeight: Integer);
+ protected
+ function DoExcludeClipRect(const ARect: TRect): Boolean; override;
+ function DoIntersectClipRect(const ARect: TRect): Boolean; override;
+ function DoUnionClipRect(const ARect: TRect): Boolean; override;
+ function DoGetClipRect: TRect; override;
+ procedure NeedBrush;
+ procedure NeedPen;
+ procedure NeedFont(ANeedFontColor: Boolean);
+ procedure NeedFontColor;
+ procedure DoDrawArc(const ARect: TRect; StartAngle, EndAngle: Single); override;
+ procedure DoDrawCircle(const ARect: TRect); override;
+ procedure DoDrawLine(const AFrom, ATo: TPoint); override;
+ procedure DoDrawPoint(const APoint: TPoint); override;
+ procedure DoFillRect(const ARect: TRect); override;
+ procedure DoTextOut(const APosition: TPoint; const AText: String); override;
+ procedure DoCopyRect(ASource: TFCustomCanvas; const ASourceRect: TRect; const ADestPos: TPoint); override;
+ procedure DoMaskedCopyRect(ASource, AMask: TFCustomCanvas; const ASourceRect: TRect; const AMaskPos, ADestPos: TPoint); override;
+ procedure DoDrawImageRect(AImage: TFCustomImage; ASourceRect: TRect; const ADestPos: TPoint); override;
+ public
+ constructor Create(AHandle: HDC);
+ destructor Destroy; override;
+ function MapColor(const AColor: TGfxColor): TGfxPixel; override;
+ function FontCellHeight: Integer; override;
+ function TextExtent(const AText: String): TSize; override;
+ procedure SaveState; override;
+ procedure RestoreState; override;
+ procedure EmptyClipRect; override;
+ procedure SetColor_(AColor: TGfxPixel); override;
+ procedure SetFont(AFont: TFCustomFont); override;
+ procedure SetLineStyle(ALineStyle: TGfxLineStyle); override;
+ property Handle: HDC read FHandle;
+ end;
+
+
+ TGDIWindowCanvas = class(TGDICanvas)
+ private
+ FWnd: HWND;
+ public
+ constructor Create(AWnd: HWND);
+ destructor Destroy; override;
+ end;
+
+
+ TGDIBitmapCanvas = class(TGDICanvas)
+ private
+ FBitmap, FOldBitmap: HBITMAP;
+ public
+ constructor Create(ABitmap: HBITMAP; AWidth, AHeight: Integer);
+ destructor Destroy; override;
+ property Bitmap: HBITMAP read FBitmap;
+ end;
+
+ { TGDIImage }
+
+ TGDIImage = class(TFCustomImage)
+ private
+ FHandle: HBITMAP;
+ IsLocked: Boolean;
+ protected
+ FStride: LongWord;
+ FData: Pointer;
+ public
+ constructor Create(AWidth, AHeight: Integer; APixelFormat: TGfxPixelFormat); override;
+ destructor Destroy; override;
+ procedure Lock(var AData: Pointer; var AStride: LongWord); override;
+ procedure Unlock; override;
+ property Handle: HBITMAP read FHandle;
+ property Stride: LongWord read FStride;
+ property Data: Pointer read FData;
+ end;
+
+ { TGDIScreen }
+
+ TGDIScreen = class(TFCustomScreen)
+ public
+ constructor Create; override;
+ end;
+
+
+ { TGDIApplication }
+
+ TGDIApplication = class(TFCustomApplication)
+ private
+ DoBreakRun: Boolean;
+ public
+ { default methods }
+ constructor Create; override;
+ destructor Destroy; override;
+ procedure AddWindow(AWindow: TFCustomWindow); override;
+ procedure Initialize(ADisplayName: String = ''); override;
+ procedure Run; override;
+ procedure Quit; override;
+ end;
+
+ { TGDIWindow }
+
+ TGDIWindow = class(TFCustomWindow)
+ private
+ FMinSize, FMaxSize: TSize;
+ FParent: TFCustomWindow;
+ { Messages }
+ procedure WMCreate(var Msg: TMessage); message WM_CREATE;
+ procedure WMDestroy(var Msg: TMessage); message WM_DESTROY;
+ procedure WMGetMinMaxInfo(var Msg: TMessage); message WM_GETMINMAXINFO;
+ procedure WMActivate(var Msg: TMessage); message WM_ACTIVATE;
+ procedure WMPaint(var Msg: TMessage); message WM_PAINT;
+ procedure WMShowWindow(var Msg: TMessage); message WM_SHOWWINDOW;
+ procedure WMMove(var Msg: TMessage); message WM_MOVE;
+ procedure WMSize(var Msg: TMessage); message WM_SIZE;
+ { Input messages }
+ procedure WMLButtonDown(var Msg: TMessage); message WM_LBUTTONDOWN;
+ procedure WMLButtonUp(var Msg: TMessage); message WM_LBUTTONUP;
+ procedure WMRButtonDown(var Msg: TMessage); message WM_RBUTTONDOWN;
+ procedure WMRButtonUp(var Msg: TMessage); message WM_RBUTTONUP;
+ procedure WMMButtonDown(var Msg: TMessage); message WM_MBUTTONDOWN;
+ procedure WMMButtonUp(var Msg: TMessage); message WM_MBUTTONUP;
+ procedure WMMouseMove(var Msg: TMessage); message WM_MOUSEMOVE;
+ procedure WMMouseWheel(var Msg: TMessage); message WM_MOUSEWHEEL;
+ procedure WMKeyDown(var Msg: TMessage); message WM_KEYDOWN;
+ procedure WMKeyUp(var Msg: TMessage); message WM_KEYUP;
+ procedure WMChar(var Msg: TMessage); message WM_CHAR;
+ procedure WMSysKeyDown(var Msg: TMessage); message WM_SYSKEYDOWN;
+ procedure WMSysKeyUp(var Msg: TMessage); message WM_SYSKEYUP;
+ procedure WMSysChar(var Msg: TMessage); message WM_SYSCHAR;
+ protected
+ WindowClass: TWndClass;
+ WindowClassW: TWndClassW;
+ FWindowStyle, FWindowStyleEx: LongWord;
+ FMouseInWindow, FHasMouseCapture, FHasFocus: Boolean;
+ function GetTitle: String; override;
+ procedure SetTitle(const ATitle: String); override;
+ procedure DoSetCursor; override;
+ procedure UpdateWindowButtons;
+ function DoMouseEnterLeaveCheck(const Msg: TMessage): Boolean;
+ public
+ constructor Create(AParent: TFCustomWindow; AWindowOptions: TGfxWindowOptions);
+ destructor Destroy; override;
+ procedure DefaultHandler(var Message); override;
+ procedure SetPosition(const APosition: TPoint); override;
+ procedure SetSize(const ASize: TSize); override;
+ procedure SetMinMaxSize(const AMinSize, AMaxSize: TSize); override;
+ procedure SetClientSize(const ASize: TSize); override;
+ procedure SetMinMaxClientSize(const AMinSize, AMaxSize: TSize); override;
+ procedure Show; override;
+ procedure Invalidate(const ARect: TRect); override;
+ procedure PaintInvalidRegion; override;
+ procedure CaptureMouse; override;
+ procedure ReleaseMouse; override;
+ end;
+
+
+function RectToWinRect(const ARect: TRect): Windows.Rect;
+function WinRectToRect(const ARect: Windows.Rect): TRect;
+
+function VirtKeyToKeycode(VirtKey: Byte): Word;
+function GetKeyboardShiftState: TShiftState;
+
+
+implementation
+
+uses
+ math, fpgfx;
+
+{
+ Use CenterPoint to get the Center-Point of any rectangle. It is primarily
+ for use with, and in, other routines such as Quadrant, and RadialPoint.
+}
+function CenterPoint(Rect : TRect) : TPoint;
+var
+ Tmp : Longint;
+begin
+ With Rect do begin
+
+ If Right < Left then begin
+ Tmp := Right;
+ Right := Left;
+ Left := Tmp;
+ end;
+
+ If Bottom < Top then begin
+ Tmp := Bottom;
+ Bottom := Top;
+ Top := Tmp;
+ end;
+
+ Result.X := Left + (Right - Left) div 2;
+ Result.Y := Top + (Bottom - Top) div 2;
+ end;
+end;
+
+
+{
+ Use LineEndPoint to get the End-Point of a line of any given Length at
+ any given angle with any given Start-Point. It is primarily for use in
+ other routines such as RadialPoint. The angle is in 1/16th of a degree.
+ For example, a full circle equals 5760 (16*360). Zero degrees is at the
+ 3'o clock position.
+}
+Function LineEndPoint(StartPoint : TPoint; Angle, Length : Extended) :
+TPoint;
+begin
+ if Angle > 360*16 then
+ Angle := Frac(Angle / 360*16) * 360*16;
+
+ if Angle < 0 then
+ Angle := 360*16 - abs(Angle);
+
+ Result.Y := StartPoint.Y - Round(Length*Sin(DegToRad(Angle/16)));
+ Result.X := StartPoint.X + Round(Length*Cos(DegToRad(Angle/16)));
+end;
+
+
+{
+ Use EllipseRadialLength to get the Radial-Length of non-rotated ellipse at
+ any given Eccentric( aka Radial ) Angle. It is primarily for use in other
+ routines such as RadialPoint. The Eccentric angle is in 1/16th of a degree.
+ For example, a full circle equals 5760 (16*360). Zero degrees is at the
+ 3'o clock position.
+}
+function EllipseRadialLength(Rect: TRect; EccentricAngle: Extended): Longint;
+var
+ a, b, R : Extended;
+begin
+ a := (Rect.Right - Rect.Left) div 2;
+ b := (Rect.Bottom - Rect.Top) div 2;
+ R := Sqr(a)*Sqr(b);
+ R := Sqrt(R / ((Sqr(b)*Sqr(Cos(DegToRad(EccentricAngle/16))))
+ + (Sqr(a)*Sqr(Sin(DegToRad(EccentricAngle/16))))));
+ Result := integer(Trunc(R));
+end;
+
+
+{
+ Use RadialPoint to get the Radial-Point at any given Eccentric( aka Radial )
+ angle on any non-rotated ellipse. It is primarily for use in Angles2Coords.
+ The EccentricAngle is in 1/16th of a degree. For example, a full circle
+ equals 5760 (16*360). Zero degrees is at the 3'o clock position.
+}
+function RadialPoint(EccentricAngle: Extended; Rect: TRect): TPoint;
+var
+ R: Longint;
+Begin
+ R := EllipseRadialLength(Rect, EccentricAngle);
+ Result := LineEndPoint(CenterPoint(Rect), EccentricAngle, R);
+end;
+
+
+{
+ Use Angles2Coords to convert an Eccentric(aka Radial) Angle and an
+ Angle-Length, such as are used in X-Windows and GTK, into the coords,
+ for Start and End Radial-Points, such as are used in the Windows API Arc
+ Pie and Chord routines. The angles 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.
+}
+procedure Angles2Coords(X, Y, Width, Height: Integer; Angle1, Angle2: Extended;
+ var SX, SY, EX, EY: Integer);
+var
+ aRect: TRect;
+ SP, EP: TPoint;
+begin
+ aRect := Rect(X, Y, X + Width, Y + Height);
+ SP := RadialPoint(Angle1, aRect);
+ If Angle2 + Angle1 > 360*16 then
+ Angle2 := (Angle2 + Angle1) - 360*16
+ else
+ Angle2 := Angle2 + Angle1;
+ EP := RadialPoint(Angle2, aRect);
+ SX := SP.X;
+ SY := SP.Y;
+ EX := EP.X;
+ EY := EP.Y;
+end;
+
+
+{ TGDIFont }
+
+class function TGDIFont.GetDefaultFontName(const AFontClass: TGfxFontClass): String;
+const
+ FontNames: array[TGfxFontClass] of String = (
+ 'Times New Roman', 'Arial', 'Courier New', 'Wingdings');
+begin
+ Result := FontNames[AFontClass];
+end;
+
+constructor TGDIFont.Create(const Descriptor: String);
+type
+ TXLFDFields = (lfdFoundry, lfdFamily, lfdWeight, lfdSlant, lfdSetWidth,
+ lfdAddStyle, lfdPixelSize, lfdPointSize, lfdResolutionX, lfdResolutionY,
+ lfdSpacing, lfdAverageWidth, lfdCharsetRegistry, lfdCharsetEncoding);
+var
+ Fields: array[TXLFDFields] of String;
+ FontInfo: LOGFONT;
+ FieldIndex: TXLFDFields;
+ s: String;
+ i: Integer;
+ dc: HDC;
+begin
+ inherited Create;
+
+ // Split the font descriptor string
+ s := Descriptor;
+ for FieldIndex := Low(TXLFDFields) to High(TXLFDFields) do
+ begin
+ Fields[FieldIndex] := Copy(s, 2, Length(s));
+ i := Pos('-', Fields[FieldIndex]);
+ if i = 0 then
+ i := Length(s);
+ Fields[FieldIndex] := Copy(Fields[FieldIndex], 1, i - 1);
+ s := Copy(s, i + 1, Length(s));
+ end;
+
+ FillChar(FontInfo, SizeOf(FontInfo), 0);
+
+ if (Length(Fields[lfdPixelSize]) > 0) and (Fields[lfdPixelSize] <> '*') then
+ FontInfo.lfHeight := -StrToInt(Fields[lfdPixelSize])
+ else if (Length(Fields[lfdPointSize]) > 0) and
+ (Fields[lfdPointSize] <> '*') then
+ begin
+ dc := Windows.GetDC(0);
+ FontInfo.lfHeight := ((StrToInt(Fields[lfdPointSize]) *
+ Windows.GetDeviceCaps(dc, LOGPIXELSY)) + (5 * 72)) div 720;
+ Windows.ReleaseDC(0, dc);
+ end;
+
+ if (Length(Fields[lfdAverageWidth]) > 0) and
+ (Fields[lfdAverageWidth] <> '*') then
+ FontInfo.lfWidth := StrToInt(Fields[lfdAverageWidth]);
+
+ if CompareText(Fields[lfdWeight], 'medium') = 0 then
+ FontInfo.lfWeight := FW_MEDIUM
+ else if CompareText(Fields[lfdWeight], 'bold') = 0 then
+ FontInfo.lfWeight := FW_BOLD;
+
+ if (CompareText(Fields[lfdSlant], 'i') = 0) or
+ (CompareText(Fields[lfdSlant], 'o') = 0) then
+ FontInfo.lfItalic := 1;
+
+ if (CompareText(Fields[lfdSpacing], 'm') = 0) or
+ (CompareText(Fields[lfdSpacing], 'c') = 0) then
+ FontInfo.lfPitchAndFamily := FIXED_PITCH
+ else if CompareText(Fields[lfdSpacing], 'p') = 0 then
+ FontInfo.lfPitchAndFamily := VARIABLE_PITCH;
+
+ if Fields[lfdFamily] <> '*' then
+ FontInfo.lfFaceName := Fields[lfdFamily];
+
+ FHandle := Windows.CreateFontIndirect(@FontInfo);
+end;
+
+
+destructor TGDIFont.Destroy;
+begin
+ Windows.DeleteObject(Handle);
+ inherited Destroy;
+end;
+
+
+{ TGDICanvas }
+
+constructor TGDICanvas.Create(AHandle: HDC);
+begin
+ inherited Create;
+ FHandle := AHandle;
+ ASSERT(Handle <> 0);
+ FDefaultFontHandle := Windows.GetStockObject(DEFAULT_GUI_FONT);
+ FCurFontHandle := FDefaultFontHandle;
+ Windows.SelectObject(Handle, FDefaultFontHandle);
+ Windows.GetTextMetrics(Handle, @FFontMetrics);
+ Windows.SetBkMode(Handle, TRANSPARENT);
+end;
+
+
+destructor TGDICanvas.Destroy;
+begin
+ if FBrush <> 0 then
+ begin
+ Windows.SelectObject(Handle, FOldBrush);
+ Windows.DeleteObject(FBrush);
+ end;
+ if FPen <> 0 then
+ begin
+ Windows.SelectObject(Handle, FOldPen);
+ Windows.DeleteObject(FPen);
+ end;
+ inherited Destroy;
+end;
+
+
+procedure TGDICanvas.SaveState;
+var
+ SavedState: PGDICanvasState;
+ NewRegion: HRGN;
+begin
+ New(SavedState);
+ SavedState^.Prev := FStateStackpointer;
+ SavedState^.Matrix := Matrix;
+ SavedState^.Color := FColor;
+ SavedState^.PenColor := FPenColor;
+ SavedState^.PenLineStyle := FPenLineStyle;
+ SavedState^.FontColor := FFontColor;
+ SavedState^.Font := FFont;
+ SavedState^.CurFontHandle := FCurFontHandle;
+ FStateStackpointer := SavedState;
+ { !!!: This is very dangerous! Some of the FCurXXX variables are not saved in
+ SavedState, which might result in graphics errors under certain
+ circumstances. Better try to remove SaveDC/RestoreDC completely. }
+ Windows.SaveDC(Handle);
+end;
+
+
+procedure TGDICanvas.RestoreState;
+var
+ SavedState: PGDICanvasState;
+begin
+ Windows.RestoreDC(Handle, -1);
+
+ SavedState := FStateStackpointer;
+ FStateStackpointer := SavedState^.Prev;
+ Matrix := SavedState^.Matrix;
+ FColor := SavedState^.Color;
+ FPenColor := SavedState^.PenColor;
+ FPenLineStyle := SavedState^.PenLineStyle;
+ FFontColor := SavedState^.FontColor;
+ FCurFontHandle := SavedState^.CurFontHandle;
+ SetFont(SavedState^.Font);
+ Dispose(SavedState);
+end;
+
+
+procedure TGDICanvas.EmptyClipRect;
+begin
+ Windows.IntersectClipRect(Handle, 0, 0, 0, 0);
+end;
+
+
+function TGDICanvas.DoExcludeClipRect(const ARect: TRect): Boolean;
+begin
+ with ARect do
+ Result :=
+ Windows.ExcludeClipRect(Handle, Left, Top, Right, Bottom) <> NULLREGION;
+end;
+
+
+function TGDICanvas.DoIntersectClipRect(const ARect: TRect): Boolean;
+begin
+ with ARect do
+ Result :=
+ Windows.IntersectClipRect(Handle, Left, Top, Right, Bottom) <> NULLREGION
+end;
+
+
+function TGDICanvas.DoUnionClipRect(const ARect: TRect): Boolean;
+var
+ Region: HRGN;
+begin
+ with ARect do
+ Region := Windows.CreateRectRgn(Left, Top, Right, Bottom);
+ Result := Windows.ExtSelectClipRgn(Handle, Region, RGN_OR) <> NULLREGION;
+ Windows.DeleteObject(Region);
+end;
+
+
+function TGDICanvas.DoGetClipRect: TRect;
+var
+ Rect: Windows.Rect;
+begin
+ Windows.GetClipBox(Handle, Rect);
+ Result := TRect(Rect);
+end;
+
+
+function TGDICanvas.MapColor(const AColor: TGfxColor): TGfxPixel;
+begin
+{ Result := Windows.GetNearestColor(Handle, RGB(AColor.Red div 257,
+ AColor.Green div 257, AColor.Blue div 257));}
+ Result := RGB(AColor.Red div 257, AColor.Green div 257, AColor.Blue div 257);
+end;
+
+
+procedure TGDICanvas.SetColor_(AColor: TGfxPixel);
+begin
+ FColor := AColor;
+end;
+
+
+procedure TGDICanvas.SetFont(AFont: TFCustomFont);
+begin
+ if AFont = FFont then
+ exit;
+
+ FFont := AFont;
+
+ if not Assigned(AFont) then
+ FFontHandle := FDefaultFontHandle
+ else
+ begin
+ if not AFont.InheritsFrom(TGDIFont) then
+ raise EGfxError.CreateFmt(SGDICanvasInvalidFontClass, [AFont.ClassName]);
+ FFontHandle := TGDIFont(AFont).Handle;
+ end;
+end;
+
+
+procedure TGDICanvas.SetLineStyle(ALineStyle: TGfxLineStyle);
+begin
+ FLineStyle := ALineStyle;
+end;
+
+
+procedure TGDICanvas.DoDrawArc(const ARect: TRect; StartAngle, EndAngle: Single);
+var
+ SX, SY, EX, EY : Longint;
+begin
+ {$Warning DoDrawArc needs testing. }
+ Angles2Coords(ARect.Left, ARect.Top, ARect.Right - ARect.Left,
+ ARect.Bottom - ARect.Top, StartAngle, EndAngle, SX, SY, EX, EY);
+ Windows.Arc(Handle, ARect.Left, ARect.Top, ARect.Right,
+ ARect.Bottom, SX, SY, EX, EY)
+end;
+
+
+procedure TGDICanvas.DoDrawCircle(const ARect: TRect);
+begin
+ {$Warning DoDrawCircle needs testing. }
+ Windows.Ellipse(Handle, ARect.Left, ARect.Top, ARect.Right, ARect.Bottom);
+end;
+
+
+procedure TGDICanvas.DoDrawLine(const AFrom, ATo: TPoint);
+begin
+ NeedPen;
+ Windows.MoveToEx(Handle, AFrom.x, AFrom.y, nil);
+ Windows.LineTo(Handle, ATo.x, ATo.y);
+end;
+
+
+procedure TGDICanvas.DoDrawPoint(const APoint: TPoint);
+begin
+ {$Warning This is not implemented yet. }
+ // Use DrawLine() as windows doesn't have a DrawPoint. It does have a
+ // SetPixel() method, but not sure how to use it yet.
+ DoDrawLine(APoint, APoint);
+// DoFillRect(Rect(APoint.X, APoint.Y, APoint.X, APoint.Y));
+end;
+
+
+procedure TGDICanvas.DoFillRect(const ARect: TRect);
+var
+ r: Windows.Rect;
+begin
+ NeedBrush;
+ r := RectToWinRect(ARect);
+ Windows.FillRect(Handle, r, FBrush);
+end;
+
+
+function TGDICanvas.FontCellHeight: Integer;
+begin
+ NeedFont(False);
+ Result := FFontMetrics.tmHeight;
+end;
+
+
+function TGDICanvas.TextExtent(const AText: String): TSize;
+var
+ WideText: WideString;
+begin
+ NeedFont(False);
+
+ WideText := Utf8Decode(AText);
+ Windows.GetTextExtentPoint32W(Handle, PWideChar(WideText), Length(WideText), @Result)
+end;
+
+
+procedure TGDICanvas.DoTextOut(const APosition: TPoint; const AText: String);
+var
+ WideText: WideString;
+begin
+ NeedFont(True);
+
+ WideText := Utf8Decode(AText);
+ Windows.TextOutW(Handle, APosition.x, APosition.y, PWideChar(WideText), Length(WideText))
+end;
+
+
+procedure TGDICanvas.DoCopyRect(ASource: TFCustomCanvas; const ASourceRect: TRect;
+ const ADestPos: TPoint);
+begin
+ if not ASource.InheritsFrom(TGDICanvas) then
+ raise EGDIError.CreateFmt(SIncompatibleCanvasForBlitting,
+ [ASource.ClassName, Self.ClassName]);
+
+ Windows.BitBlt(
+ Handle, ADestPos.x, ADestPos.y, ASourceRect.Right - ASourceRect.Left,
+ ASourceRect.Bottom - ASourceRect.Top,
+ TGDICanvas(ASource).Handle, ASourceRect.Left, ASourceRect.Top,
+ SRCCOPY);
+end;
+
+
+procedure TGDICanvas.DoMaskedCopyRect(ASource, AMask: TFCustomCanvas;
+ const ASourceRect: TRect; const AMaskPos, ADestPos: TPoint);
+var
+ w, h: Integer;
+ SourceBitmap, AndObjectBitmap, AndMemBitmap, SaveBitmap,
+ OldSourceBitmap, OldAndObjectBitmap, OldAndMemBitmap,
+ OldSaveBitmap: HBITMAP;
+ SourceDC, MemDC, ObjectDC, SaveDC: HDC;
+begin
+ if not ASource.InheritsFrom(TGDICanvas) then
+ raise EGDIError.CreateFmt(SIncompatibleCanvasForBlitting,
+ [ASource.ClassName, Self.ClassName]);
+
+ if not AMask.InheritsFrom(TGDICanvas) then
+ raise EGDIError.CreateFmt(SIncompatibleCanvasForBlitting,
+ [AMask.ClassName, Self.ClassName]);
+
+ w := ASourceRect.Right - ASourceRect.Left;
+ h := ASourceRect.Bottom - ASourceRect.Top;
+
+ // See http://support.microsoft.com/support/kb/articles/Q79/2/12.ASP
+
+ SourceDC := Windows.CreateCompatibleDC(Handle);
+ ObjectDC := Windows.CreateCompatibleDC(Handle);
+ MemDC := Windows.CreateCompatibleDC(Handle);
+ SourceBitmap := Windows.CreateCompatibleBitmap(Handle, w, h);
+ AndObjectBitmap := Windows.CreateCompatibleBitmap(ObjectDC, w, h);
+ AndMemBitmap := Windows.CreateCompatibleBitmap(Handle, w, h);
+ OldSourceBitmap := Windows.SelectObject(SourceDC, SourceBitmap);
+ OldAndObjectBitmap := Windows.SelectObject(ObjectDC, AndObjectBitmap);
+ OldAndMemBitmap := Windows.SelectObject(MemDC, AndMemBitmap);
+
+ Windows.BitBlt(SourceDC, 0, 0, w, h,
+ TGDICanvas(ASource).Handle, ASourceRect.Left, ASourceRect.Top, SRCCOPY);
+ Windows.BitBlt(MemDC, 0, 0, w, h, Handle, ADestPos.x, ADestPos.y, SRCCOPY);
+
+ // !!!: Find a ROP for replacing the following 2 Blits with a single one:
+ Windows.BitBlt(ObjectDC, 0, 0, w, h,
+ TGDICanvas(AMask).Handle, AMaskPos.x, AMaskPos.y, NOTSRCCOPY);
+ Windows.BitBlt(MemDC, 0, 0, w, h, ObjectDC, 0, 0, SRCAND);
+
+ Windows.BitBlt(SourceDC, 0, 0, w, h,
+ TGDICanvas(AMask).Handle, AMaskPos.x, AMaskPos.y, SRCAND);
+ Windows.BitBlt(MemDC, 0, 0, w, h, SourceDC, 0, 0, SRCPAINT);
+ // Copy the result to the screen
+ Windows.BitBlt(Handle, ADestPos.x, ADestPos.y, w, h, MemDC, 0, 0, SRCCOPY);
+
+ // Clean up
+ Windows.DeleteObject(Windows.SelectObject(ObjectDC, OldAndObjectBitmap));
+ Windows.DeleteObject(Windows.SelectObject(MemDC, OldAndMemBitmap));
+ Windows.DeleteObject(Windows.SelectObject(SourceDC, OldSourceBitmap));
+ Windows.DeleteDC(MemDC);
+ Windows.DeleteDC(ObjectDC);
+ Windows.DeleteDC(SourceDC);
+end;
+
+
+procedure TGDICanvas.DoDrawImageRect(AImage: TFCustomImage; ASourceRect: TRect;
+ const ADestPos: TPoint);
+var
+ MemDC: HDC;
+ OldBitmap: HBITMAP;
+ GDIPal: array of PRGBQUAD;
+ i: Integer;
+begin
+ ASSERT(AImage.InheritsFrom(TGDIImage));
+ {$IFDEF Debug}
+ ASSERT(not TGDIImage(AImage).IsLocked);
+ {$ENDIF}
+
+ MemDC := Windows.CreateCompatibleDC(Handle);
+ OldBitmap := Windows.SelectObject(MemDC, TGDIImage(AImage).Handle);
+
+ // Set the color palette, if present
+ if Assigned(AImage.Palette) then
+ begin
+ GetMem(GDIPal, AImage.Palette.EntryCount * SizeOf(RGBQUAD));
+ for i := 0 to AImage.Palette.EntryCount - 1 do
+ with AImage.Palette.Entries[i] do
+ begin
+ GDIPal[i].rgbRed := Red div 257;
+ GDIPal[i].rgbGreen := Green div 257;
+ GDIPal[i].rgbBlue := Blue div 257;
+ GDIPal[i].rgbReserved := 0;
+ end;
+ Windows.SetDIBColorTable(MemDC, 0, AImage.Palette.EntryCount, GDIPal[0]^);
+ FreeMem(GDIPal);
+ end;
+
+ with ASourceRect do
+ Windows.BitBlt(Handle, ADestPos.x, ADestPos.y, Right - Left, Bottom - Top,
+ MemDC, Left, Top, SRCCOPY);
+
+ Windows.SelectObject(MemDC, OldBitmap);
+ Windows.DeleteDC(MemDC);
+end;
+
+
+procedure TGDICanvas.NeedBrush;
+begin
+ if (FBrush = 0) or (FBrushColor <> FColor) then
+ begin
+ if FBrush <> 0 then
+ begin
+ Windows.SelectObject(Handle, FOldBrush);
+ Windows.DeleteObject(FBrush);
+ end;
+ FBrushColor := FColor;
+ FBrush := Windows.CreateSolidBrush(FBrushColor);
+ FOldBrush := Windows.SelectObject(Handle, FBrush);
+ end;
+end;
+
+
+procedure TGDICanvas.NeedPen;
+begin
+ if (FPen = 0) or (FPenColor <> FColor) or (FPenLineStyle <> FLineStyle) then
+ begin
+ if FPen <> 0 then
+ begin
+ Windows.SelectObject(Handle, FOldPen);
+ Windows.DeleteObject(FPen);
+ end;
+ FPenColor := FColor;
+ FPenLineStyle := FLineStyle;
+ case FPenLineStyle of
+ lsSolid:
+ FPen := Windows.CreatePen(PS_SOLID, 0, FPenColor);
+ end;
+ FOldPen := Windows.SelectObject(Handle, FPen);
+ end;
+end;
+
+
+procedure TGDICanvas.NeedFont(ANeedFontColor: Boolean);
+begin
+ if FCurFontHandle <> FFontHandle then
+ begin
+ Windows.SelectObject(Handle, FFontHandle);
+ { TODO : Store the font metrics in TGDIFont }
+ Windows.GetTextMetrics(Handle, @FFontMetrics);
+ FCurFontHandle := FFontHandle;
+ end;
+ if ANeedFontColor then
+ NeedFontColor;
+end;
+
+
+procedure TGDICanvas.NeedFontColor;
+begin
+ if FFontColor <> FColor then
+ begin
+ FFontColor := FColor;
+ Windows.SetTextColor(Handle, FFontColor);
+ end;
+end;
+
+
+procedure TGDICanvas.Resized(NewWidth, NewHeight: Integer);
+begin
+ FWidth := NewWidth;
+ FHeight := NewHeight;
+end;
+
+
+{ TGDIWindowCanvas }
+
+constructor TGDIWindowCanvas.Create(AWnd: HWND);
+begin
+ FWnd := AWnd;
+ inherited Create(Windows.GetDC(FWnd));
+end;
+
+
+destructor TGDIWindowCanvas.Destroy;
+begin
+ inherited Destroy;
+ if Handle <> 0 then
+ Windows.ReleaseDC(FWnd, Handle);
+end;
+
+
+{ TGDIBitmapCanvas }
+
+constructor TGDIBitmapCanvas.Create(ABitmap: HBITMAP; AWidth, AHeight: Integer);
+begin
+ ASSERT(ABitmap <> 0);
+ FBitmap := ABitmap;
+ inherited Create(Windows.CreateCompatibleDC(0));
+ FWidth := AWidth;
+ FHeight := AHeight;
+ FOldBitmap := Windows.SelectObject(Handle, Bitmap);
+end;
+
+
+destructor TGDIBitmapCanvas.Destroy;
+begin
+ Windows.SelectObject(Handle, FOldBitmap);
+ Windows.DeleteObject(Bitmap);
+ Windows.DeleteDC(Handle);
+ inherited Destroy;
+end;
+
+
+{ TGDIImage }
+
+constructor TGDIImage.Create(AWidth, AHeight: Integer; APixelFormat: TGfxPixelFormat);
+var
+ BitmapInfo: PBitmapInfo;
+ Color: PRGBQUAD;
+ TempDC: HDC;
+begin
+ inherited Create(AWidth, AHeight, APixelFormat);
+
+ case APixelFormat.FormatType of
+ ftMono:
+ begin
+ FStride := (AWidth + 7) shr 3;
+ GetMem(BitmapInfo, SizeOf(TBitmapInfoHeader) + 2 * SizeOf(RGBQUAD));
+ BitmapInfo^.bmiHeader.biClrUsed := 2;
+ Color := @BitmapInfo^.bmiColors[0];
+ Color^.rgbRed := 0;
+ Color^.rgbGreen := 0;
+ Color^.rgbBlue := 0;
+ Color^.rgbReserved := 0;
+ Inc(Color);
+ Color^.rgbRed := 255;
+ Color^.rgbGreen := 255;
+ Color^.rgbBlue := 255;
+ Color^.rgbReserved := 0;
+ end;
+ ftPal4, ftPal4A:
+ begin
+ FStride := (AWidth + 1) shr 1;
+ GetMem(BitmapInfo, SizeOf(TBitmapInfoHeader) + 16 * SizeOf(RGBQUAD));
+ BitmapInfo^.bmiHeader.biClrUsed := 0;
+ end;
+ ftPal8, ftPal8A:
+ begin
+ FStride := AWidth;
+ GetMem(BitmapInfo, SizeOf(TBitmapInfoHeader) + 256 * SizeOf(RGBQUAD));
+ BitmapInfo^.bmiHeader.biClrUsed := 0;
+ end;
+ else
+ begin
+ FStride := AWidth * (FormatTypeBPPTable[APixelFormat.FormatType] shr 3);
+ GetMem(BitmapInfo, SizeOf(TBitmapInfoHeader));
+ BitmapInfo^.bmiHeader.biClrUsed := 0;
+ end;
+ end;
+ // The stride is always a multiple of 4
+ FStride := (FStride + 3) and not 3;
+
+ with BitmapInfo^.bmiHeader do
+ begin
+ biSize := SizeOf(TBitmapInfoHeader);
+ biWidth := AWidth;
+ biHeight := AHeight;
+ biPlanes := 1;
+ biBitCount := FormatTypeBPPTable[APixelFormat.FormatType];
+ biCompression := 0;
+ biSizeImage := 4 * AHeight * AWidth;
+ biXPelsPerMeter := 0;
+ biYPelsPerMeter := 0;
+ biClrImportant := 0;
+ end;
+
+ FData := nil;
+
+ TempDC := GetDC(0);
+ FHandle := Windows.CreateDIBSection(TempDC, BitmapInfo^, DIB_RGB_COLORS, FData, 0, 0);
+ ReleaseDC(0, TempDC);
+
+ FreeMem(BitmapInfo);
+end;
+
+
+destructor TGDIImage.Destroy;
+begin
+ if Handle <> 0 then
+ Windows.DeleteObject(Handle);
+ inherited Destroy;
+end;
+
+
+procedure TGDIImage.Lock(var AData: Pointer; var AStride: LongWord);
+begin
+ ASSERT(not IsLocked);
+ IsLocked := True;
+ AData := Data;
+ AStride := Stride;
+ Windows.GdiFlush;
+end;
+
+procedure TGDIImage.Unlock;
+begin
+ ASSERT(IsLocked);
+ IsLocked := False;
+end;
+
+
+{ TGDIScreen }
+
+constructor TGDIScreen.Create;
+begin
+ inherited Create;
+
+end;
+
+{ TGDIApplication }
+
+constructor TGDIApplication.Create;
+begin
+ inherited Create;
+
+end;
+
+
+destructor TGDIApplication.Destroy;
+var
+ i: Integer;
+begin
+ for i := 0 to Forms.Count - 1 do
+ TGDIWindow(Forms[i]).Free;
+
+ inherited Destroy;
+end;
+
+procedure TGDIApplication.AddWindow(AWindow: TFCustomWindow);
+begin
+ Forms.Add(AWindow);
+end;
+
+
+procedure TGDIApplication.Initialize(ADisplayName: String);
+begin
+
+end;
+
+procedure TGDIApplication.Run;
+var
+ Msg: TMsg;
+begin
+ DoBreakRun := False;
+
+ while Windows.GetMessage(@Msg, 0, 0, 0) and
+ (not (QuitWhenLastWindowCloses and (Forms.Count = 0))) and
+ (DoBreakRun = False) do
+ begin
+ Windows.TranslateMessage(@msg);
+ Windows.DispatchMessage(@msg);
+ end;
+
+ DoBreakRun := False;
+end;
+
+
+procedure TGDIApplication.Quit;
+begin
+ DoBreakRun := True;
+end;
+
+
+{ TGDIWindow }
+
+function fpGFXWindowProc(hwnd: HWND; uMsg: UINT; wParam: WPARAM;
+ lParam: LPARAM): LRESULT; stdcall;
+var
+ Window: TGDIWindow;
+ Msg: TMessage;
+begin
+ if uMsg = WM_CREATE then
+ begin
+ Window := TGDIWindow(PCreateStruct(lParam)^.lpCreateParams);
+ Window.FHandle := hwnd;
+ Windows.SetWindowLong(hwnd, GWL_USERDATA, LongWord(Window));
+ end else
+ Window := TGDIWindow(Windows.GetWindowLong(hwnd, GWL_USERDATA));
+
+ if Assigned(Window) then
+ begin
+ Msg.msg := uMsg;
+ Msg.wParam := wParam;
+ Msg.lParam := lParam;
+ Msg.Result := 0;
+ Window.Dispatch(Msg);
+ Result := Msg.Result;
+ end
+ else
+ if UnicodeEnabledOS then Result := Windows.DefWindowProcW(hwnd, uMsg, wParam, lParam)
+ else Result := Windows.DefWindowProc(hwnd, uMsg, wParam, lParam);
+end;
+
+
+constructor TGDIWindow.Create(AParent: TFCustomWindow; AWindowOptions: TGfxWindowOptions);
+var
+ ParentHandle: HWND;
+begin
+ inherited Create;
+
+ FWindowOptions := AWindowOptions;
+ FParent := AParent;
+
+ { Initialize a window class, if necessary }
+ if UnicodeEnabledOS then
+ begin
+ if not Assigned(WindowClassW.lpfnWndProc) then
+ begin
+ WindowClassW.style := CS_HREDRAW or CS_VREDRAW;
+ WindowClassW.lpfnWndProc := WndProc(@fpGFXWindowProc);
+ WindowClassW.hInstance := MainInstance;
+ WindowClassW.hIcon := LoadIcon(0, IDI_APPLICATION);
+ WindowClassW.hCursor := LoadCursor(0, IDC_ARROW);
+ WindowClassW.hbrBackground := 0;
+ WindowClassW.lpszClassName := 'fpGFX';
+ end;
+ Windows.RegisterClassW(@WindowClassW);
+ end
+ else
+ begin
+ if not Assigned(WindowClass.lpfnWndProc) then
+ begin
+ WindowClass.style := CS_HREDRAW or CS_VREDRAW;
+ WindowClass.lpfnWndProc := WndProc(@fpGFXWindowProc);
+ WindowClass.hInstance := MainInstance;
+ WindowClass.hIcon := LoadIcon(0, IDI_APPLICATION);
+ WindowClass.hCursor := LoadCursor(0, IDC_ARROW);
+ WindowClass.hbrBackground := 0;
+ WindowClass.lpszClassName := 'fpGFX';
+ end;
+ Windows.RegisterClass(@WindowClass);
+ end;
+
+ if Assigned(AParent) then
+ ParentHandle := AParent.Handle
+ else
+ ParentHandle := 0;
+
+ if (woBorderless in FWindowOptions) and (woPopUp in FWindowOptions) then FWindowStyle := WS_POPUP
+ else if woPopUp in FWindowOptions then FWindowStyle := WS_POPUPWINDOW
+ else if woToolWindow in FWindowOptions then FWindowStyle := WS_OVERLAPPED
+ else if woChildWindow in FWindowOptions then FWindowStyle := WS_CHILDWINDOW
+ else if woBorderless in FWindowOptions then FWindowStyle := WS_OVERLAPPED
+ else if woWindow in FWindowOptions then FWindowStyle := WS_OVERLAPPEDWINDOW
+ else FWindowStyle := 0;
+
+ if woPopUp in FWindowOptions then FWindowStyleEx := WS_EX_TOOLWINDOW
+ else if woToolWindow in FWindowOptions then FWindowStyleEx := WS_EX_TOOLWINDOW
+ else if woWindow in FWindowOptions then FWindowStyleEx := WS_EX_APPWINDOW
+ else FWindowStyleEx := 0;
+
+ if UnicodeEnabledOS then
+ FHandle := Windows.CreateWindowExW(
+ FWindowStyleEx, // extended window style
+ 'fpGFX', // registered class name
+ 'fpGFX Window', // window name
+ FWindowStyle, // window style
+ CW_USEDEFAULT, // horizontal position of window
+ CW_USEDEFAULT, // vertical position of window
+ CW_USEDEFAULT, // window width
+ CW_USEDEFAULT, // window height
+ ParentHandle, // handle to parent or owner window
+ 0, // menu handle or child identifier
+ MainInstance, // handle to application instance
+ Self) // window-creation data
+ else
+ FHandle := Windows.CreateWindowEx(
+ FWindowStyleEx, // extended window style
+ 'fpGFX', // registered class name
+ 'fpGFX Window', // window name
+ FWindowStyle, // window style
+ CW_USEDEFAULT, // horizontal position of window
+ CW_USEDEFAULT, // vertical position of window
+ CW_USEDEFAULT, // window width
+ CW_USEDEFAULT, // window height
+ ParentHandle, // handle to parent or owner window
+ 0, // menu handle or child identifier
+ MainInstance, // handle to application instance
+ Self); // window-creation data
+
+ FCanvas := TGDIWindowCanvas.Create(Handle);
+end;
+
+
+destructor TGDIWindow.Destroy;
+var
+ OldHandle: HWND;
+begin
+ if Assigned(OnClose) then
+ OnClose(Self);
+
+ Canvas.Free;
+
+ if Handle <> 0 then
+ begin
+ OldHandle := Handle;
+ FHandle := 0;
+ Windows.DestroyWindow(OldHandle);
+ end;
+
+ gApplication.Forms.Remove(Self);
+
+ // Are we the last window for our owning display?
+ if gApplication.Forms.Count = 0 then
+ Windows.PostQuitMessage(0);
+
+ inherited Destroy;
+end;
+
+
+procedure TGDIWindow.DefaultHandler(var Message);
+begin
+ if UnicodeEnabledOS then
+ TMessage(Message).Result := Windows.DefWindowProcW(Handle,
+ TMessage(Message).Msg, TMessage(Message).wParam, TMessage(Message).lParam)
+ else
+ TMessage(Message).Result := Windows.DefWindowProc(Handle,
+ TMessage(Message).Msg, TMessage(Message).wParam, TMessage(Message).lParam)
+end;
+
+
+procedure TGDIWindow.SetPosition(const APosition: TPoint);
+begin
+ Windows.SetWindowPos(Handle, 0, APosition.x, APosition.y, 0, 0,
+ SWP_NOSIZE or SWP_NOZORDER);
+end;
+
+
+procedure TGDIWindow.SetSize(const ASize: TSize);
+begin
+ if (ASize.cx <> Width) or (ASize.cy <> Height) then
+ Windows.SetWindowPos(Handle, 0, 0, 0, ASize.cx, ASize.cy,
+ SWP_NOMOVE or SWP_NOZORDER);
+end;
+
+
+procedure TGDIWindow.SetMinMaxSize(const AMinSize, AMaxSize: TSize);
+begin
+ FMinSize := AMinSize;
+ FMaxSize := AMaxSize;
+ UpdateWindowButtons;
+end;
+
+
+procedure TGDIWindow.SetClientSize(const ASize: TSize);
+var
+ r: Windows.Rect;
+begin
+ if (ASize.cx <> ClientWidth) or (ASize.cx <> ClientHeight) then
+ begin
+ r.Left := 0;
+ r.Top := 0;
+ r.Right := ASize.cx;
+ r.Bottom := ASize.cy;
+ Windows.AdjustWindowRectEx(r, FWindowStyle, False, FWindowStyleEx);
+ SetSize(Size(WinRectToRect(r)));
+ end;
+end;
+
+
+procedure TGDIWindow.SetMinMaxClientSize(const AMinSize, AMaxSize: TSize);
+var
+ Rect: Windows.Rect;
+begin
+ Rect.Left := 0;
+ Rect.Top := 0;
+ Rect.Right := AMinSize.cx;
+ Rect.Bottom := AMinSize.cy;
+ Windows.AdjustWindowRectEx(Rect, FWindowStyle, False, FWindowStyleEx);
+ if AMinSize.cx > 0 then
+ FMinSize.cx := Rect.Right - Rect.Left
+ else
+ FMinSize.cx := 0;
+ if AMinSize.cy > 0 then
+ FMinSize.cy := Rect.Bottom - Rect.Top
+ else
+ FMinSize.cy := 0;
+
+ Rect.Left := 0;
+ Rect.Top := 0;
+ Rect.Right := AMaxSize.cx;
+ Rect.Bottom := AMaxSize.cy;
+ Windows.AdjustWindowRectEx(Rect, FWindowStyle, False, FWindowStyleEx);
+ if AMaxSize.cx > 0 then
+ FMaxSize.cx := Rect.Right - Rect.Left
+ else
+ FMaxSize.cx := 0;
+ if AMaxSize.cy > 0 then
+ FMaxSize.cy := Rect.Bottom - Rect.Top
+ else
+ FMaxSize.cy := 0;
+
+ UpdateWindowButtons;
+end;
+
+
+procedure TGDIWindow.Show;
+begin
+ Windows.ShowWindow(Handle, SW_SHOWNORMAL);
+ Windows.UpdateWindow(Handle);
+end;
+
+
+procedure TGDIWindow.Invalidate(const ARect: TRect);
+var
+ Rect: Windows.Rect;
+begin
+ Rect.Left := ARect.Left;
+ Rect.Top := ARect.Top;
+ Rect.Right := ARect.Right;
+ Rect.Bottom := ARect.Bottom;
+ Windows.InvalidateRect(Handle, Rect, False);
+end;
+
+
+procedure TGDIWindow.PaintInvalidRegion;
+begin
+ Windows.UpdateWindow(Handle);
+end;
+
+
+procedure TGDIWindow.CaptureMouse;
+begin
+ if FHasMouseCapture then
+ exit;
+
+ FHasMouseCapture := True;
+
+ if not FMouseInWindow then
+ begin
+ FMouseInWindow := True;
+ Windows.SetCapture(Handle);
+ end;
+end;
+
+
+procedure TGDIWindow.ReleaseMouse;
+begin
+ if FHasMouseCapture then
+ begin
+ FHasMouseCapture := False;
+ if not FMouseInWindow then
+ begin
+ Windows.ReleaseCapture;
+ end;
+ end;
+end;
+
+
+function TGDIWindow.GetTitle: String;
+var
+ TextLen: Integer;
+ AnsiBuffer: string;
+ WideBuffer: WideString;
+begin
+ if UnicodeEnabledOS then
+ begin
+ TextLen := Windows.GetWindowTextLengthW(Handle);
+ SetLength(WideBuffer, TextLen);
+ TextLen := Windows.GetWindowTextW(Handle, @WideBuffer[1], TextLen + 1);
+ SetLength(WideBuffer, TextLen);
+ Result := Utf8Encode(WideBuffer);
+ end
+ else
+ begin
+ TextLen := Windows.GetWindowTextLength(Handle);
+ SetLength(AnsiBuffer, TextLen);
+ TextLen := Windows.GetWindowText(Handle, @AnsiBuffer[1], TextLen + 1);
+ SetLength(AnsiBuffer, TextLen);
+ Result := AnsiToUtf8(AnsiBuffer);
+ end;
+end;
+
+procedure TGDIWindow.SetTitle(const ATitle: String);
+begin
+ if UnicodeEnabledOS then
+ Windows.SetWindowTextW(Handle, PWideChar(Utf8Decode(ATitle)))
+ else Windows.SetWindowText(Handle, PChar(Utf8ToAnsi(ATitle)));
+end;
+
+
+procedure TGDIWindow.DoSetCursor;
+const
+ CursorTable: array[TGfxCursor] of Integer = (
+ 32512, // crDefault
+ 0, // crNone
+ 32512, // crArrow
+ 32515, // crCross
+ 32513, // crIBeam
+ 32646, // crSize
+ 32645, // crSizeNS
+ 32644, // crSizeWE
+ 32516, // crUpArrow
+ 32514, // crHourGlass
+ 32648, // crNoDrop
+ 32651); // crHelp
+var
+ ID: Integer;
+begin
+ if FMouseInWindow then
+ begin
+ ID := CursorTable[Cursor];
+ if ID <> 0 then
+ Windows.SetCursor(Windows.LoadCursor(0, MAKEINTRESOURCE(ID)))
+ else
+ Windows.SetCursor(0);
+ end;
+end;
+
+
+procedure TGDIWindow.UpdateWindowButtons;
+var
+ CanMaximize: Boolean;
+begin
+ if woWindow in FWindowOptions then
+ begin
+ CanMaximize := (FMaxSize.cx = 0) or (FMaxSize.cy = 0) or
+ (FMaxSize.cx > FMinSize.cx) or (FMaxSize.cy > FMinSize.cy);
+
+ if CanMaximize and ((FWindowStyle and WS_MAXIMIZEBOX) = 0) then
+ FWindowStyle := FWindowStyle or WS_MAXIMIZEBOX
+ else if (not CanMaximize) and
+ ((FWindowStyle and WS_MAXIMIZEBOX) <> 0) then
+ FWindowStyle := FWindowStyle and not WS_MAXIMIZEBOX;
+
+ Windows.SetWindowLong(Handle, GWL_STYLE, FWindowStyle or
+ (Windows.GetWindowLong(Handle, GWL_STYLE) and
+ (WS_MAXIMIZE or WS_MINIMIZE or WS_VISIBLE))); // preserver these bits!
+ end;
+end;
+
+
+function WindowFromPoint(x, y: Windows.LONG):Windows.HWND; external 'user32' name 'WindowFromPoint';
+
+
+function TGDIWindow.DoMouseEnterLeaveCheck(const Msg: TMessage): Boolean;
+
+ function CursorInDifferentWindow: Boolean;
+ var
+ pt: Windows.POINT;
+ begin
+ pt.x := Msg.lParamLo;
+ pt.y := Msg.lParamHi;
+
+ // only WM_MOUSEWHEEL uses screen coordinates!!!
+ if Msg.Msg <> WM_MOUSEWHEEL then
+ Windows.ClientToScreen(Handle, pt);
+
+ Result := WindowFromPoint(pt.x, pt.y) <> Handle;
+{!!!: Result := Windows.WindowFromPoint(pt) <> Handle;}
+ end;
+
+var
+ pt: Windows.POINT;
+begin
+ if not FMouseInWindow then
+ begin
+ FMouseInWindow := True;
+ DoSetCursor;
+ Windows.SetCapture(Handle);
+ if Assigned(OnMouseEnter) then
+ OnMouseEnter(Self, GetKeyboardShiftState,
+ Point(Msg.lParamLo, Msg.lParamHi));
+ Result := Msg.Msg <> WM_MOUSEMOVE;
+ end else
+ begin
+ pt.x := Msg.lParamLo;
+ pt.y := Msg.lParamHi;
+ if Msg.Msg = WM_MOUSEWHEEL then
+ Windows.ScreenToClient(Handle, pt);
+ if (pt.x < 0) or (pt.y < 0) or (pt.x >= ClientWidth) or
+ (pt.y >= ClientHeight) or CursorInDifferentWindow then
+ FMouseInWindow := False;
+
+ if (not FHasMouseCapture) and (not FMouseInWindow) then
+ begin
+ Windows.ReleaseCapture;
+ if Assigned(OnMouseLeave) then
+ OnMouseLeave(Self);
+ Result := False;
+ end else
+ Result := True;
+ end;
+end;
+
+
+// private methods
+
+procedure TGDIWindow.WMCreate(var Msg: TMessage);
+begin
+ if Assigned(OnCreate) then
+ OnCreate(Self);
+end;
+
+
+procedure TGDIWindow.WMDestroy(var Msg: TMessage);
+begin
+ if Handle <> 0 then
+ Self.Free;
+end;
+
+
+procedure TGDIWindow.WMGetMinMaxInfo(var Msg: TMessage);
+begin
+ with PMinMaxInfo(Msg.lParam)^ do
+ begin
+ if FMinSize.cx > 0 then
+ ptMinTrackSize.x := FMinSize.cx;
+ if FMinSize.cy > 0 then
+ ptMinTrackSize.y := FMinSize.cy;
+ if FMaxSize.cx > 0 then
+ ptMaxTrackSize.x := FMaxSize.cx;
+ if FMaxSize.cy > 0 then
+ ptMaxTrackSize.y := FMaxSize.cy;
+ end;
+end;
+
+
+procedure TGDIWindow.WMActivate(var Msg: TMessage);
+begin
+ if Msg.wParam = WA_INACTIVE then
+ begin
+ FHasFocus := False;
+ if Assigned(OnFocusOut) then
+ OnFocusOut(Self);
+ end else
+ begin
+ FHasFocus := True;
+ if Assigned(OnFocusIn) then
+ OnFocusIn(Self);
+ end;
+end;
+
+
+procedure TGDIWindow.WMPaint(var Msg: TMessage);
+var
+ PaintStruct: TPaintStruct;
+ r: TRect;
+ OldCanvas: TFCustomCanvas;
+begin
+ Windows.BeginPaint(Handle, @PaintStruct);
+ if Assigned(OnPaint) then
+ begin
+ with PaintStruct.rcPaint do
+ begin
+ r.Left := Left;
+ r.Top := Top;
+ r.Right := Right;
+ r.Bottom := Bottom;
+ end;
+ OldCanvas := Canvas;
+ FCanvas := TGDICanvas.Create(PaintStruct.hdc);
+ OnPaint(Self, r);
+ Canvas.Free;
+ FCanvas := OldCanvas;
+ end;
+ Windows.EndPaint(Handle, @PaintStruct);
+end;
+
+
+procedure TGDIWindow.WMShowWindow(var Msg: TMessage);
+begin
+ if Msg.wParam <> 0 then
+ begin
+ if Assigned(OnFocusIn) then
+ OnFocusIn(Self);
+ if Assigned(OnShow) then
+ OnShow(Self);
+ end else
+ if Assigned(OnHide) then
+ OnHide(Self);
+end;
+
+
+procedure TGDIWindow.WMMove(var Msg: TMessage);
+begin
+ if (Msg.lParamLo <> Left) or (Msg.lParamHi <> Top) then
+ begin
+ FLeft := Msg.lParamLo;
+ FTop := Msg.lParamHi;
+ if Assigned(OnMove) then
+ OnMove(Self);
+ end;
+end;
+
+
+procedure TGDIWindow.WMSize(var Msg: TMessage);
+var
+ r: Windows.Rect;
+begin
+ if (Msg.lParamLo <> ClientWidth) or (Msg.lParamHi <> ClientHeight) then
+ begin
+ Windows.GetWindowRect(Handle, r);
+ FWidth := r.Right - r.Left;
+ FHeight := r.Bottom - r.Top;
+ Windows.GetClientRect(Handle, r);
+ FClientWidth := Msg.lParamLo;
+ FClientHeight := Msg.lParamHi;
+ TGDICanvas(Canvas).Resized(FWidth, FHeight);
+ if Assigned(OnResize) then
+ OnResize(Self);
+ end;
+end;
+
+
+procedure TGDIWindow.WMLButtonDown(var Msg: TMessage);
+begin
+ if FMouseInWindow and not FHasFocus then
+ Windows.SetActiveWindow(Handle);
+ if DoMouseEnterLeaveCheck(Msg) and Assigned(OnMousePressed) then
+ OnMousePressed(Self, mbLeft, GetKeyboardShiftState,
+ Point(Msg.lParamLo, Msg.lParamHi));
+end;
+
+
+procedure TGDIWindow.WMLButtonUp(var Msg: TMessage);
+begin
+ if DoMouseEnterLeaveCheck(Msg) and Assigned(OnMouseReleased) then
+ OnMouseReleased(Self, mbLeft, GetKeyboardShiftState,
+ Point(Msg.lParamLo, Msg.lParamHi));
+end;
+
+
+procedure TGDIWindow.WMRButtonDown(var Msg: TMessage);
+begin
+ if FMouseInWindow and not FHasFocus then
+ Windows.SetActiveWindow(Handle);
+ if DoMouseEnterLeaveCheck(Msg) and Assigned(OnMousePressed) then
+ OnMousePressed(Self, mbRight, GetKeyboardShiftState,
+ Point(Msg.lParamLo, Msg.lParamHi));
+end;
+
+
+procedure TGDIWindow.WMRButtonUp(var Msg: TMessage);
+begin
+ if DoMouseEnterLeaveCheck(Msg) and Assigned(OnMouseReleased) then
+ OnMouseReleased(Self, mbRight, GetKeyboardShiftState,
+ Point(Msg.lParamLo, Msg.lParamHi));
+end;
+
+
+procedure TGDIWindow.WMMButtonDown(var Msg: TMessage);
+begin
+ if FMouseInWindow and not FHasFocus then
+ Windows.SetActiveWindow(Handle);
+ if DoMouseEnterLeaveCheck(Msg) and Assigned(OnMousePressed) then
+ OnMousePressed(Self, mbMiddle, GetKeyboardShiftState,
+ Point(Msg.lParamLo, Msg.lParamHi));
+end;
+
+procedure TGDIWindow.WMMButtonUp(var Msg: TMessage);
+begin
+ if DoMouseEnterLeaveCheck(Msg) and Assigned(OnMouseReleased) then
+ OnMouseReleased(Self, mbMiddle, GetKeyboardShiftState,
+ Point(Msg.lParamLo, Msg.lParamHi));
+end;
+
+procedure TGDIWindow.WMMouseMove(var Msg: TMessage);
+begin
+ if DoMouseEnterLeaveCheck(Msg) and Assigned(OnMouseMove) then
+ OnMouseMove(Self, GetKeyboardShiftState, Point(Msg.lParamLo, Msg.lParamHi));
+end;
+
+procedure TGDIWindow.WMMouseWheel(var Msg: TMessage);
+var
+ pt: Windows.POINT;
+begin
+ if DoMouseEnterLeaveCheck(Msg) and Assigned(OnMouseWheel) then
+ begin
+ pt.x := Msg.lParamLo;
+ pt.y := Msg.lParamHi;
+ Windows.ScreenToClient(Handle, pt);
+ OnMouseWheel(Self, GetKeyboardShiftState, SmallInt(Msg.wParamHi) / -120.0,
+ Point(pt.x, pt.y));
+ end;
+end;
+
+procedure TGDIWindow.WMKeyDown(var Msg: TMessage);
+begin
+ if Assigned(OnKeyPressed) then
+ OnKeyPressed(Self, VirtKeyToKeycode(Msg.wParam), GetKeyboardShiftState);
+ if (Msg.wParam = $2e {VK_DELETE}) and Assigned(OnKeyChar) then
+ OnKeyChar(Self, #127);
+end;
+
+procedure TGDIWindow.WMKeyUp(var Msg: TMessage);
+begin
+ if Assigned(OnKeyReleased) then
+ OnKeyReleased(Self, VirtKeyToKeycode(Msg.wParam), GetKeyboardShiftState);
+end;
+
+procedure TGDIWindow.WMChar(var Msg: TMessage);
+begin
+ if Assigned(OnKeyChar) then
+ OnKeyChar(Self, Chr(Msg.wParam));
+end;
+
+procedure TGDIWindow.WMSysKeyDown(var Msg: TMessage);
+begin
+ WMKeyDown(Msg);
+end;
+
+procedure TGDIWindow.WMSysKeyUp(var Msg: TMessage);
+begin
+ WMKeyUp(Msg);
+end;
+
+procedure TGDIWindow.WMSysChar(var Msg: TMessage);
+begin
+ WMChar(Msg);
+end;
+
+
+{ Helpers }
+
+function RectToWinRect(const ARect: TRect): Windows.Rect;
+begin
+ Result.Left := ARect.Left;
+ Result.Top := ARect.Top;
+ Result.Right := ARect.Right;
+ Result.Bottom := ARect.Bottom;
+end;
+
+function WinRectToRect(const ARect: Windows.Rect): TRect;
+begin
+ Result.Left := ARect.Left;
+ Result.Top := ARect.Top;
+ Result.Right := ARect.Right;
+ Result.Bottom := ARect.Bottom;
+end;
+
+
+{$INCLUDE gdikeys.inc}
+
+initialization
+
+ WinVersion.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
+ GetVersionEx(WinVersion);
+
+ UnicodeEnabledOS := (WinVersion.dwPlatformID = VER_PLATFORM_WIN32_NT)
+ or (WinVersion.dwPlatformID = VER_PLATFORM_WIN32_CE);
+
+end.
+