/units/RxVCLUtils.pas

https://bitbucket.org/stden/rxlib · Pascal · 3048 lines · 2382 code · 255 blank · 411 comment · 207 complexity · 12622ff4455c33e89948da180096c006 MD5 · raw file

  1. {*******************************************************}
  2. { }
  3. { Delphi VCL Extensions (RX) }
  4. { }
  5. { Copyright (c) 1995, 1996 AO ROSNO }
  6. { Copyright (c) 1997, 1998 Master-Bank }
  7. { }
  8. { Patched by Polaris Software }
  9. { Revision and functions added by JB. }
  10. {*******************************************************}
  11. unit RxVCLUtils;
  12. {$I RX.INC}
  13. {$P+,W-,R-,V-}
  14. {$IFDEF RX_D6}
  15. {$WARN SYMBOL_PLATFORM OFF} // Polaris
  16. {$ENDIF}
  17. interface
  18. uses Windows, Classes, Graphics, Forms, Controls, Dialogs, Math, RxMaxMin;
  19. { Windows resources (bitmaps and icons) VCL-oriented routines }
  20. procedure DrawBitmapTransparent(Dest: TCanvas; DstX, DstY: Integer;
  21. Bitmap: TBitmap; TransparentColor: TColor);
  22. procedure DrawBitmapRectTransparent(Dest: TCanvas; DstX, DstY: Integer;
  23. SrcRect: TRect; Bitmap: TBitmap; TransparentColor: TColor);
  24. procedure StretchBitmapRectTransparent(Dest: TCanvas; DstX, DstY, DstW,
  25. DstH: Integer; SrcRect: TRect; Bitmap: TBitmap; TransparentColor: TColor);
  26. function MakeBitmap(ResID: PChar): TBitmap;
  27. function MakeBitmapID(ResID: Word): TBitmap;
  28. function MakeModuleBitmap(Module: THandle; ResID: PChar): TBitmap;
  29. function CreateTwoColorsBrushPattern(Color1, Color2: TColor): TBitmap;
  30. {$IFDEF RX_D9}inline;{$ENDIF}
  31. function CreateDisabledBitmapEx(FOriginal: TBitmap; OutlineColor, BackColor,
  32. HighlightColor, ShadowColor: TColor; DrawHighlight: Boolean): TBitmap;
  33. function CreateDisabledBitmap(FOriginal: TBitmap; OutlineColor: TColor): TBitmap;
  34. {$IFDEF RX_D9}inline;{$ENDIF}
  35. function ChangeBitmapColor(Bitmap: TBitmap; Color, NewColor: TColor): TBitmap;
  36. {$IFDEF RX_D9}inline;{$ENDIF}
  37. procedure AssignBitmapCell(Source: TGraphic; Dest: TBitmap; Cols, Rows,
  38. Index: Integer); {$IFDEF RX_D9}inline;{$ENDIF}
  39. {$IFNDEF VER80}
  40. procedure ImageListDrawDisabled(Images: TImageList; Canvas: TCanvas;
  41. X, Y, Index: Integer; HighlightColor, GrayColor: TColor; DrawHighlight: Boolean);
  42. {$ENDIF}
  43. function MakeIcon(ResID: PChar): TIcon;
  44. function MakeIconID(ResID: Word): TIcon;
  45. function MakeModuleIcon(Module: THandle; ResID: PChar): TIcon;
  46. function CreateBitmapFromIcon(Icon: TIcon; BackColor: TColor): TBitmap;
  47. {$IFNDEF VER80}
  48. function CreateIconFromBitmap(Bitmap: TBitmap; TransparentColor: TColor): TIcon;
  49. {$ENDIF}
  50. { Service routines }
  51. procedure NotImplemented;
  52. procedure ResourceNotFound(ResID: PChar);
  53. function PointInRect(const P: TPoint; const R: TRect): Boolean; {$IFDEF RX_D9}inline;{$ENDIF}
  54. function PointInPolyRgn(const P: TPoint; const Points: array of TPoint): Boolean;
  55. function PaletteColor(Color: TColor): Longint; {$IFDEF RX_D9}inline;{$ENDIF}
  56. function WidthOf(R: TRect): Integer; {$IFDEF RX_D9}inline;{$ENDIF}
  57. function HeightOf(R: TRect): Integer; {$IFDEF RX_D9}inline;{$ENDIF}
  58. procedure PaintInverseRect(const RectOrg, RectEnd: TPoint); {$IFDEF RX_D9}inline;{$ENDIF}
  59. procedure DrawInvertFrame(ScreenRect: TRect; Width: Integer); {$IFDEF RX_D9}inline;{$ENDIF}
  60. procedure CopyParentImage(Control: TControl; Dest: TCanvas);
  61. procedure Delay(MSecs: Longint);
  62. procedure CenterControl(Control: TControl);
  63. {$IFNDEF VER80}
  64. procedure ShowMDIClientEdge(ClientHandle: THandle; ShowEdge: Boolean);
  65. function MakeVariant(const Values: array of Variant): Variant;
  66. {$ENDIF}
  67. function CreateRotatedFont(Font: TFont; Angle: Integer): HFont;
  68. function MsgBox(const Caption, Text: string; Flags: Integer): Integer;
  69. function MsgDlg(const Msg: string; AType: TMsgDlgType;
  70. AButtons: TMsgDlgButtons; HelpCtx: Longint): Word;
  71. {$IFDEF CBUILDER}
  72. function FindPrevInstance(const MainFormClass: ShortString;
  73. const ATitle: string): HWnd;
  74. function ActivatePrevInstance(const MainFormClass: ShortString;
  75. const ATitle: string): Boolean;
  76. {$ELSE}
  77. function FindPrevInstance(const MainFormClass, ATitle: string): HWnd;
  78. function ActivatePrevInstance(const MainFormClass, ATitle: string): Boolean;
  79. {$ENDIF CBUILDER}
  80. function IsForegroundTask: Boolean;
  81. procedure MergeForm(AControl: TWinControl; AForm: TForm; Align: TAlign;
  82. Show: Boolean);
  83. function GetAveCharSize(Canvas: TCanvas): TPoint; {$IFDEF RX_D9}inline;{$ENDIF}
  84. function MinimizeText(const Text: string; Canvas: TCanvas;
  85. MaxWidth: Integer): string; {$IFDEF RX_D9}inline;{$ENDIF}
  86. procedure FreeUnusedOle;
  87. procedure Beep;
  88. function GetWindowsVersion: string;
  89. function LoadDLL(const LibName: string): THandle;
  90. function RegisterServer(const ModuleName: string): Boolean;
  91. {$IFDEF VER80}
  92. function IsLibrary: Boolean;
  93. {$ENDIF}
  94. { Gradient filling routine }
  95. type
  96. TFillDirection = (fdTopToBottom, fdBottomToTop, fdLeftToRight, fdRightToLeft);
  97. procedure GradientFillRect(Canvas: TCanvas; ARect: TRect; StartColor,
  98. EndColor: TColor; Direction: TFillDirection; Colors: Byte); {$IFDEF RX_D9}inline;{$ENDIF}
  99. { String routines }
  100. function GetEnvVar(const VarName: string): string;
  101. function AnsiUpperFirstChar(const S: string): string;
  102. function StringToPChar(var S: string): PChar;
  103. function StrPAlloc(const S: string): PChar;
  104. procedure SplitCommandLine(const CmdLine: string; var ExeName,
  105. Params: string);
  106. function DropT(const S: string): string;
  107. { Memory routines }
  108. function AllocMemo(Size: Longint): Pointer;
  109. function ReallocMemo(fpBlock: Pointer; Size: Longint): Pointer;
  110. procedure FreeMemo(var fpBlock: Pointer);
  111. function GetMemoSize(fpBlock: Pointer): Longint;
  112. function CompareMem(fpBlock1, fpBlock2: Pointer; Size: Cardinal): Boolean;
  113. {$IFNDEF RX_D5}
  114. procedure FreeAndNil(var Obj);
  115. {$ENDIF}
  116. { Manipulate huge pointers routines }
  117. procedure HugeInc(var HugePtr: Pointer; Amount: Longint);
  118. procedure HugeDec(var HugePtr: Pointer; Amount: Longint);
  119. function HugeOffset(HugePtr: Pointer; Amount: Longint): Pointer;
  120. procedure HugeMove(Base: Pointer; Dst, Src, Size: Longint);
  121. {$IFNDEF VER80}
  122. procedure HMemCpy(DstPtr, SrcPtr: Pointer; Amount: Longint);
  123. {$ELSE}
  124. procedure ZeroMemory(Ptr: Pointer; Length: Longint);
  125. procedure FillMemory(Ptr: Pointer; Length: Longint; Fill: Byte);
  126. {$ENDIF}
  127. { Standard Windows colors that are not defined by Delphi }
  128. const
  129. {$IFDEF VER80}
  130. clInfoBk = TColor($02E1FFFF);
  131. clNone = TColor($02FFFFFF);
  132. {$ENDIF}
  133. clCream = TColor($A6CAF0);
  134. clMoneyGreen = TColor($C0DCC0);
  135. clSkyBlue = TColor($FFFBF0);
  136. clMedGray = TColor($A4A0A0);
  137. { ModalResult constants }
  138. {$IFNDEF RX_D3}
  139. const
  140. mrNoToAll = mrAll + 1;
  141. mrYesToAll = mrNoToAll + 1;
  142. {$ENDIF}
  143. {$IFNDEF RX_D4}
  144. { Mouse Wheel message }
  145. {$IFNDEF VER80}
  146. {$IFDEF VER90}
  147. const
  148. WM_MOUSEWHEEL = $020A;
  149. WHEEL_DELTA = 120;
  150. WHEEL_PAGESCROLL = MAXDWORD;
  151. SM_MOUSEWHEELPRESENT = 75;
  152. MOUSEEVENTF_WHEEL = $0800;
  153. SPI_GETWHEELSCROLLLINES = 104;
  154. SPI_SETWHEELSCROLLLINES = 105;
  155. {$ENDIF}
  156. type
  157. TWMMouseWheel = record
  158. Msg: Cardinal;
  159. Keys: Word;
  160. Delta: Word;
  161. case Integer of
  162. 0: (
  163. XPos: Smallint;
  164. YPos: Smallint);
  165. 1: (
  166. Pos: TSmallPoint;
  167. Result: Longint);
  168. end;
  169. {$ENDIF}
  170. {$ENDIF RX_D4}
  171. { Cursor routines }
  172. const
  173. WaitCursor: TCursor = crHourGlass;
  174. procedure StartWait;
  175. procedure StopWait;
  176. function DefineCursor(Instance: THandle; ResID: PChar): TCursor;
  177. {$IFNDEF VER80}
  178. function LoadAniCursor(Instance: THandle; ResID: PChar): HCursor;
  179. {$ENDIF}
  180. { Windows API level routines }
  181. procedure StretchBltTransparent(DstDC: HDC; DstX, DstY, DstW, DstH: Integer;
  182. SrcDC: HDC; SrcX, SrcY, SrcW, SrcH: Integer; Palette: HPalette;
  183. TransparentColor: TColorRef); {$IFDEF RX_D9}inline;{$ENDIF}
  184. procedure DrawTransparentBitmap(DC: HDC; Bitmap: HBitmap;
  185. DstX, DstY: Integer; TransparentColor: TColorRef);
  186. function PaletteEntries(Palette: HPALETTE): Integer;
  187. function WindowClassName(Wnd: HWnd): string;
  188. function ScreenWorkArea: TRect;
  189. {$IFDEF VER80}
  190. procedure MoveWindowOrg(DC: HDC; DX, DY: Integer);
  191. {$ENDIF}
  192. procedure SwitchToWindow(Wnd: HWnd; Restore: Boolean);
  193. procedure ActivateWindow(Wnd: HWnd);
  194. procedure ShowWinNoAnimate(Handle: HWnd; CmdShow: Integer);
  195. procedure CenterWindow(Wnd: HWnd);
  196. procedure ShadeRect(DC: HDC; const Rect: TRect);
  197. procedure KillMessage(Wnd: HWnd; Msg: Cardinal);
  198. { Convert dialog units to pixels and backwards }
  199. function DialogUnitsToPixelsX(DlgUnits: Word): Word;
  200. function DialogUnitsToPixelsY(DlgUnits: Word): Word;
  201. function PixelsToDialogUnitsX(PixUnits: Word): Word;
  202. function PixelsToDialogUnitsY(PixUnits: Word): Word;
  203. { Grid drawing }
  204. type
  205. TVertAlignment = (vaTopJustify, vaCenter, vaBottomJustify);
  206. procedure WriteText(ACanvas: TCanvas; ARect: TRect; DX, DY: Integer;
  207. const Text: string; Alignment: TAlignment; WordWrap: Boolean
  208. {$IFDEF RX_D4}; ARightToLeft: Boolean = False {$ENDIF});
  209. procedure DrawCellText(Control: TCustomControl; ACol, ARow: Longint;
  210. const S: string; const ARect: TRect; Align: TAlignment;
  211. VertAlign: TVertAlignment); {$IFDEF RX_D4} overload; {$ENDIF} {$IFDEF RX_D9}inline;{$ENDIF}
  212. procedure DrawCellTextEx(Control: TCustomControl; ACol, ARow: Longint;
  213. const S: string; const ARect: TRect; Align: TAlignment;
  214. VertAlign: TVertAlignment; WordWrap: Boolean); {$IFDEF RX_D4} overload; {$ENDIF}
  215. {$IFDEF RX_D4}
  216. procedure DrawCellText(Control: TCustomControl; ACol, ARow: Longint;
  217. const S: string; const ARect: TRect; Align: TAlignment;
  218. VertAlign: TVertAlignment; ARightToLeft: Boolean); overload;
  219. procedure DrawCellTextEx(Control: TCustomControl; ACol, ARow: Longint;
  220. const S: string; const ARect: TRect; Align: TAlignment;
  221. VertAlign: TVertAlignment; WordWrap: Boolean; ARightToLeft: Boolean); overload;
  222. {$ENDIF}
  223. procedure DrawCellBitmap(Control: TCustomControl; ACol, ARow: Longint;
  224. Bmp: TGraphic; Rect: TRect);
  225. { TScreenCanvas }
  226. type
  227. TScreenCanvas = class(TCanvas)
  228. private
  229. FDeviceContext: HDC;
  230. protected
  231. procedure CreateHandle; override;
  232. public
  233. destructor Destroy; override;
  234. procedure SetOrigin(X, Y: Integer);
  235. procedure FreeHandle;
  236. end;
  237. {$IFDEF VER80}
  238. { TBits }
  239. TBits = class
  240. private
  241. FSize: Integer;
  242. FBits: Pointer;
  243. procedure SetSize(Value: Integer);
  244. procedure SetBit(Index: Integer; Value: Boolean);
  245. function GetBit(Index: Integer): Boolean;
  246. public
  247. destructor Destroy; override;
  248. function OpenBit: Integer;
  249. property Bits[Index: Integer]: Boolean read GetBit write SetBit; default;
  250. property Size: Integer read FSize write SetSize;
  251. end;
  252. { TMetafileCanvas }
  253. TMetafileCanvas = class(TCanvas)
  254. private
  255. FMetafile: TMetafile;
  256. public
  257. constructor Create(AMetafile: TMetafile; ReferenceDevice: HDC);
  258. destructor Destroy; override;
  259. property Metafile: TMetafile read FMetafile;
  260. end;
  261. { TResourceStream }
  262. TResourceStream = class(THandleStream)
  263. private
  264. FStartPos: LongInt;
  265. FEndPos: LongInt;
  266. protected
  267. constructor CreateFromPChar(Instance: THandle; ResName, ResType: PChar);
  268. public
  269. constructor Create(Instance: THandle; const ResName: string; ResType: PChar);
  270. constructor CreateFromID(Instance: THandle; ResID: Integer; ResType: PChar);
  271. destructor Destroy; override;
  272. function Seek(Offset: Longint; Origin: Word): Longint; override;
  273. function Write(const Buffer; Count: Longint): Longint; override;
  274. end;
  275. function GetCurrentDir: string;
  276. function SetCurrentDir(const Dir: string): Boolean;
  277. {$ENDIF}
  278. {$IFNDEF VER80}
  279. function CheckWin32(OK: Boolean): Boolean; { obsolete, use Win32Check }
  280. {$IFNDEF RX_D3}
  281. function Win32Check(RetVal: Bool): Bool;
  282. {$ENDIF}
  283. procedure RaiseWin32Error(ErrorCode: DWORD);
  284. {$ENDIF}
  285. {$IFNDEF RX_D3} { for Delphi 3.0 and previous versions compatibility }
  286. type
  287. TCustomForm = TForm;
  288. TDate = TDateTime;
  289. TTime = TDateTime;
  290. function ResStr(Ident: Cardinal): string;
  291. {$ELSE}
  292. function ResStr(const Ident: string): string;
  293. {$ENDIF RX_D3}
  294. {$IFNDEF RX_D4}
  295. type
  296. Longword = Longint;
  297. {$ENDIF}
  298. function TextSizeDC(DC: HDC; const Text: string): TSize;
  299. function TextSize(Wnd: HWnd; const Text: string): TSize;
  300. function TextToLinesDC(DC: HDC; const Text: string; MaxLen: Integer): string;
  301. function TextToLines(Wnd: HWnd; const Text: string; MaxLen: Integer): string;
  302. { force OS management}
  303. function LogOffWindows(Force: Boolean): Boolean;
  304. function PowerOffComputer(Force: Boolean): Boolean;
  305. function RebootComputer(Force: Boolean): Boolean;
  306. function ShutdownComputer(Force: Boolean): Boolean;
  307. { low level routine }
  308. function DownWindows(Flags: UINT): Boolean;
  309. implementation
  310. Uses SysUtils, Messages, Consts, RxConst, {$IFDEF RX_V110} SysConst, {$ENDIF}
  311. {$IFDEF RX_D6} RTLConsts, Variants, {$ENDIF} // Polaris
  312. {$IFDEF RX_D12} Character, {$ENDIF} RxStrUtils,
  313. {$IFDEF RX_D16} System.UITypes, {$ENDIF}
  314. {$IFNDEF VER80} CommCtrl, {$ELSE} Str16, {$ENDIF} RxResConst ;
  315. { force OS management}
  316. function DownWindows(Flags: UINT): Boolean;
  317. var
  318. TokenPriv: TTokenPrivileges;
  319. TokenHandle: THandle;
  320. begin
  321. Result := False;
  322. if (Win32Platform = VER_PLATFORM_WIN32_NT) and ((Flags and (EWX_POWEROFF or EWX_REBOOT or EWX_SHUTDOWN)) <> 0) then
  323. begin
  324. if OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES, TokenHandle) then
  325. if LookupPrivilegeValue(nil, 'SeShutdownPrivilege', TokenPriv.Privileges[0].LUID) then
  326. begin
  327. TokenPriv.PrivilegeCount := 1;
  328. TokenPriv.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
  329. if not AdjustTokenPrivileges(TokenHandle, False, TokenPriv, 0, TTokenPrivileges(nil^), DWORD(nil^)) then
  330. exit;
  331. end;
  332. end;
  333. Result := ExitWindowsEx(Flags, 0);
  334. end;
  335. const
  336. ForceFlag: array [Boolean] of UINT = (0, EWX_FORCE);
  337. function LogOffWindows(Force: Boolean): Boolean;
  338. begin
  339. Result := DownWindows(EWX_LOGOFF or ForceFlag[Force]);
  340. end;
  341. function PowerOffComputer(Force: Boolean): Boolean;
  342. begin
  343. Result := DownWindows(EWX_POWEROFF or ForceFlag[Force]);
  344. end;
  345. function RebootComputer(Force: Boolean): Boolean;
  346. begin
  347. Result := DownWindows(EWX_REBOOT or ForceFlag[Force]);
  348. end;
  349. function ShutdownComputer(Force: Boolean): Boolean;
  350. begin
  351. Result := DownWindows(EWX_SHUTDOWN or ForceFlag[Force]);
  352. end;
  353. { Exceptions }
  354. procedure ResourceNotFound(ResID: PChar);
  355. var
  356. S: string;
  357. begin
  358. {$IFDEF WIN64}
  359. if Int64Rec(ResID).Hi = 0 then S := IntToStr(Int64Rec(ResID).Lo)
  360. else S := StrPas(ResID);
  361. {$ELSE}
  362. if LongRec(ResID).Hi = 0 then S := IntToStr(LongRec(ResID).Lo)
  363. else S := StrPas(ResID);
  364. {$ENDIF}
  365. raise EResNotFound.CreateFmt(ResStr(SResNotFound), [S]);
  366. end;
  367. { Bitmaps }
  368. function MakeModuleBitmap(Module: THandle; ResID: PChar): TBitmap;
  369. {$IFDEF VER80}
  370. var
  371. S: TStream;
  372. {$ENDIF}
  373. begin
  374. Result := TBitmap.Create;
  375. try
  376. {$IFNDEF VER80}
  377. if Module <> 0 then
  378. begin
  379. {$IFDEF WIN64}
  380. if Int64Rec(ResID).Hi = 0 then
  381. Result.LoadFromResourceID(Module, Int64Rec(ResID).Lo)
  382. else
  383. Result.LoadFromResourceName(Module, StrPas(ResID));
  384. {$ELSE}
  385. if LongRec(ResID).Hi = 0 then
  386. Result.LoadFromResourceID(Module, LongRec(ResID).Lo)
  387. else
  388. Result.LoadFromResourceName(Module, StrPas(ResID));
  389. {$ENDIF}
  390. end
  391. else begin
  392. Result.Handle := LoadBitmap(Module, ResID);
  393. if Result.Handle = 0 then ResourceNotFound(ResID);
  394. end;
  395. {$ELSE}
  396. Result.Handle := LoadBitmap(Module, ResID);
  397. if Result.Handle = 0 then ResourceNotFound(ResID);
  398. {$ENDIF}
  399. except
  400. Result.Free;
  401. Result := nil;
  402. end;
  403. end;
  404. function MakeBitmap(ResID: PChar): TBitmap;
  405. begin
  406. Result := MakeModuleBitmap(hInstance, ResID);
  407. end;
  408. function MakeBitmapID(ResID: Word): TBitmap;
  409. begin
  410. Result := MakeModuleBitmap(hInstance, MakeIntResource(ResID));
  411. end;
  412. procedure AssignBitmapCell(Source: TGraphic; Dest: TBitmap; Cols, Rows,
  413. Index: Integer);
  414. var
  415. CellWidth, CellHeight: Integer;
  416. begin
  417. if (Source <> nil) and (Dest <> nil) then
  418. begin
  419. if Cols <= 0 then Cols := 1;
  420. if Rows <= 0 then Rows := 1;
  421. if Index < 0 then Index := 0;
  422. CellWidth := Source.Width div Cols;
  423. CellHeight := Source.Height div Rows;
  424. with Dest do
  425. begin
  426. Width := CellWidth; Height := CellHeight;
  427. end;
  428. if Source is TBitmap then
  429. begin
  430. Dest.Canvas.CopyRect(Bounds(0, 0, CellWidth, CellHeight),
  431. TBitmap(Source).Canvas, Bounds((Index mod Cols) * CellWidth,
  432. (Index div Cols) * CellHeight, CellWidth, CellHeight));
  433. {$IFDEF RX_D3}
  434. Dest.TransparentColor := TBitmap(Source).TransparentColor;
  435. {$ENDIF RX_D3}
  436. end
  437. else
  438. begin
  439. Dest.Canvas.Brush.Color := clSilver;
  440. Dest.Canvas.FillRect(Bounds(0, 0, CellWidth, CellHeight));
  441. Dest.Canvas.Draw(-(Index mod Cols) * CellWidth,
  442. -(Index div Cols) * CellHeight, Source);
  443. end;
  444. {$IFDEF RX_D3}
  445. Dest.Transparent := Source.Transparent;
  446. {$ENDIF RX_D3}
  447. end;
  448. end;
  449. type
  450. TParentControl = class(TWinControl);
  451. procedure CopyParentImage(Control: TControl; Dest: TCanvas);
  452. var
  453. I, Count, X, Y, SaveIndex: Integer;
  454. DC: HDC;
  455. R, SelfR, CtlR: TRect;
  456. begin
  457. if (Control = nil) or (Control.Parent = nil) then Exit;
  458. Count := Control.Parent.ControlCount;
  459. DC := Dest.Handle;
  460. {$IFNDEF VER80}
  461. with Control.Parent do ControlState := ControlState + [csPaintCopy];
  462. try
  463. {$ENDIF}
  464. with Control do
  465. begin
  466. SelfR := Bounds(Left, Top, Width, Height);
  467. X := -Left; Y := -Top;
  468. end;
  469. { Copy parent control image }
  470. SaveIndex := SaveDC(DC);
  471. try
  472. SetViewportOrgEx(DC, X, Y, nil);
  473. IntersectClipRect(DC, 0, 0, Control.Parent.ClientWidth,
  474. Control.Parent.ClientHeight);
  475. with TParentControl(Control.Parent) do
  476. begin
  477. Perform(WM_ERASEBKGND, DC, 0);
  478. PaintWindow(DC);
  479. end;
  480. finally
  481. RestoreDC(DC, SaveIndex);
  482. end;
  483. { Copy images of graphic controls }
  484. for I := 0 to Count - 1 do
  485. begin
  486. if Control.Parent.Controls[I] = Control then Break
  487. else if (Control.Parent.Controls[I] <> nil) and (Control.Parent.Controls[I] is TGraphicControl) then
  488. begin
  489. with TGraphicControl(Control.Parent.Controls[I]) do
  490. begin
  491. CtlR := Bounds(Left, Top, Width, Height);
  492. if Bool(IntersectRect(R, SelfR, CtlR)) and Visible then
  493. begin
  494. {$IFNDEF VER80}
  495. ControlState := ControlState + [csPaintCopy];
  496. {$ENDIF}
  497. SaveIndex := SaveDC(DC);
  498. try
  499. SaveIndex := SaveDC(DC);
  500. SetViewportOrgEx(DC, Left + X, Top + Y, nil);
  501. IntersectClipRect(DC, 0, 0, Width, Height);
  502. Perform(WM_PAINT, DC, 0);
  503. finally
  504. RestoreDC(DC, SaveIndex);
  505. {$IFNDEF VER80}
  506. ControlState := ControlState - [csPaintCopy];
  507. {$ENDIF}
  508. end;
  509. end;
  510. end;
  511. end;
  512. end;
  513. {$IFNDEF VER80}
  514. finally
  515. with Control.Parent do ControlState := ControlState - [csPaintCopy];
  516. end;
  517. {$ENDIF}
  518. end;
  519. { Transparent bitmap }
  520. procedure StretchBltTransparent(DstDC: HDC; DstX, DstY, DstW, DstH: Integer;
  521. SrcDC: HDC; SrcX, SrcY, SrcW, SrcH: Integer; Palette: HPalette;
  522. TransparentColor: TColorRef);
  523. var
  524. Color: TColorRef;
  525. bmAndBack, bmAndObject, bmAndMem, bmSave: HBitmap;
  526. bmBackOld, bmObjectOld, bmMemOld, bmSaveOld: HBitmap;
  527. MemDC, BackDC, ObjectDC, SaveDC: HDC;
  528. palDst, palMem, palSave, palObj: HPalette;
  529. begin
  530. { Create some DCs to hold temporary data }
  531. BackDC := CreateCompatibleDC(DstDC);
  532. ObjectDC := CreateCompatibleDC(DstDC);
  533. MemDC := CreateCompatibleDC(DstDC);
  534. SaveDC := CreateCompatibleDC(DstDC);
  535. { Create a bitmap for each DC }
  536. bmAndObject := CreateBitmap(SrcW, SrcH, 1, 1, nil);
  537. bmAndBack := CreateBitmap(SrcW, SrcH, 1, 1, nil);
  538. bmAndMem := CreateCompatibleBitmap(DstDC, DstW, DstH);
  539. bmSave := CreateCompatibleBitmap(DstDC, SrcW, SrcH);
  540. { Each DC must select a bitmap object to store pixel data }
  541. bmBackOld := SelectObject(BackDC, bmAndBack);
  542. bmObjectOld := SelectObject(ObjectDC, bmAndObject);
  543. bmMemOld := SelectObject(MemDC, bmAndMem);
  544. bmSaveOld := SelectObject(SaveDC, bmSave);
  545. { Select palette }
  546. palDst := 0; palMem := 0; palSave := 0; palObj := 0;
  547. if Palette <> 0 then
  548. begin
  549. palDst := SelectPalette(DstDC, Palette, True);
  550. RealizePalette(DstDC);
  551. palSave := SelectPalette(SaveDC, Palette, False);
  552. RealizePalette(SaveDC);
  553. palObj := SelectPalette(ObjectDC, Palette, False);
  554. RealizePalette(ObjectDC);
  555. palMem := SelectPalette(MemDC, Palette, True);
  556. RealizePalette(MemDC);
  557. end;
  558. { Set proper mapping mode }
  559. SetMapMode(SrcDC, GetMapMode(DstDC));
  560. SetMapMode(SaveDC, GetMapMode(DstDC));
  561. { Save the bitmap sent here }
  562. BitBlt(SaveDC, 0, 0, SrcW, SrcH, SrcDC, SrcX, SrcY, SRCCOPY);
  563. { Set the background color of the source DC to the color, }
  564. { contained in the parts of the bitmap that should be transparent }
  565. Color := SetBkColor(SaveDC, PaletteColor(TransparentColor));
  566. { Create the object mask for the bitmap by performing a BitBlt() }
  567. { from the source bitmap to a monochrome bitmap }
  568. BitBlt(ObjectDC, 0, 0, SrcW, SrcH, SaveDC, 0, 0, SRCCOPY);
  569. { Set the background color of the source DC back to the original }
  570. SetBkColor(SaveDC, Color);
  571. { Create the inverse of the object mask }
  572. BitBlt(BackDC, 0, 0, SrcW, SrcH, ObjectDC, 0, 0, NOTSRCCOPY);
  573. { Copy the background of the main DC to the destination }
  574. BitBlt(MemDC, 0, 0, DstW, DstH, DstDC, DstX, DstY, SRCCOPY);
  575. { Mask out the places where the bitmap will be placed }
  576. StretchBlt(MemDC, 0, 0, DstW, DstH, ObjectDC, 0, 0, SrcW, SrcH, SRCAND);
  577. { Mask out the transparent colored pixels on the bitmap }
  578. BitBlt(SaveDC, 0, 0, SrcW, SrcH, BackDC, 0, 0, SRCAND);
  579. { XOR the bitmap with the background on the destination DC }
  580. StretchBlt(MemDC, 0, 0, DstW, DstH, SaveDC, 0, 0, SrcW, SrcH, SRCPAINT);
  581. { Copy the destination to the screen }
  582. BitBlt(DstDC, DstX, DstY, DstW, DstH, MemDC, 0, 0,
  583. SRCCOPY);
  584. { Restore palette }
  585. if Palette <> 0 then
  586. begin
  587. SelectPalette(MemDC, palMem, False);
  588. SelectPalette(ObjectDC, palObj, False);
  589. SelectPalette(SaveDC, palSave, False);
  590. SelectPalette(DstDC, palDst, True);
  591. end;
  592. { Delete the memory bitmaps }
  593. DeleteObject(SelectObject(BackDC, bmBackOld));
  594. DeleteObject(SelectObject(ObjectDC, bmObjectOld));
  595. DeleteObject(SelectObject(MemDC, bmMemOld));
  596. DeleteObject(SelectObject(SaveDC, bmSaveOld));
  597. { Delete the memory DCs }
  598. DeleteDC(MemDC);
  599. DeleteDC(BackDC);
  600. DeleteDC(ObjectDC);
  601. DeleteDC(SaveDC);
  602. end;
  603. procedure DrawTransparentBitmapRect(DC: HDC; Bitmap: HBitmap; DstX, DstY,
  604. DstW, DstH: Integer; SrcRect: TRect; TransparentColor: TColorRef);
  605. var
  606. hdcTemp: HDC;
  607. begin
  608. hdcTemp := CreateCompatibleDC(DC);
  609. try
  610. SelectObject(hdcTemp, Bitmap);
  611. with SrcRect do
  612. StretchBltTransparent(DC, DstX, DstY, DstW, DstH, hdcTemp,
  613. Left, Top, Right - Left, Bottom - Top, 0, TransparentColor);
  614. finally
  615. DeleteDC(hdcTemp);
  616. end;
  617. end;
  618. procedure DrawTransparentBitmap(DC: HDC; Bitmap: HBitmap;
  619. DstX, DstY: Integer; TransparentColor: TColorRef);
  620. var
  621. BM: {$IFNDEF VER80} Windows.TBitmap {$ELSE} WinTypes.TBitmap {$ENDIF};
  622. begin
  623. GetObject(Bitmap, SizeOf(BM), @BM);
  624. DrawTransparentBitmapRect(DC, Bitmap, DstX, DstY, BM.bmWidth, BM.bmHeight,
  625. Rect(0, 0, BM.bmWidth, BM.bmHeight), TransparentColor);
  626. end;
  627. procedure StretchBitmapTransparent(Dest: TCanvas; Bitmap: TBitmap;
  628. TransparentColor: TColor; DstX, DstY, DstW, DstH, SrcX, SrcY,
  629. SrcW, SrcH: Integer);
  630. var
  631. CanvasChanging: TNotifyEvent;
  632. begin
  633. if DstW <= 0 then DstW := Bitmap.Width;
  634. if DstH <= 0 then DstH := Bitmap.Height;
  635. if (SrcW <= 0) or (SrcH <= 0) then
  636. begin
  637. SrcX := 0; SrcY := 0;
  638. SrcW := Bitmap.Width;
  639. SrcH := Bitmap.Height;
  640. end;
  641. if not Bitmap.Monochrome then
  642. SetStretchBltMode(Dest.Handle, STRETCH_DELETESCANS);
  643. CanvasChanging := Bitmap.Canvas.OnChanging;
  644. {$IFDEF RX_D3}
  645. Bitmap.Canvas.Lock;
  646. {$ENDIF}
  647. try
  648. Bitmap.Canvas.OnChanging := nil;
  649. if TransparentColor = clNone then
  650. begin
  651. StretchBlt(Dest.Handle, DstX, DstY, DstW, DstH, Bitmap.Canvas.Handle,
  652. SrcX, SrcY, SrcW, SrcH, Dest.CopyMode);
  653. end
  654. else
  655. begin
  656. {$IFDEF RX_D3}
  657. if TransparentColor = clDefault then
  658. TransparentColor := Bitmap.Canvas.Pixels[0, Bitmap.Height - 1];
  659. {$ENDIF}
  660. if Bitmap.Monochrome then TransparentColor := clWhite
  661. else TransparentColor := ColorToRGB(TransparentColor);
  662. StretchBltTransparent(Dest.Handle, DstX, DstY, DstW, DstH,
  663. Bitmap.Canvas.Handle, SrcX, SrcY, SrcW, SrcH, Bitmap.Palette,
  664. TransparentColor);
  665. end;
  666. finally
  667. Bitmap.Canvas.OnChanging := CanvasChanging;
  668. {$IFDEF RX_D3}
  669. Bitmap.Canvas.Unlock;
  670. {$ENDIF}
  671. end;
  672. end;
  673. procedure StretchBitmapRectTransparent(Dest: TCanvas; DstX, DstY,
  674. DstW, DstH: Integer; SrcRect: TRect; Bitmap: TBitmap;
  675. TransparentColor: TColor);
  676. begin
  677. with SrcRect do
  678. StretchBitmapTransparent(Dest, Bitmap, TransparentColor,
  679. DstX, DstY, DstW, DstH, Left, Top, Right - Left, Bottom - Top);
  680. end;
  681. procedure DrawBitmapRectTransparent(Dest: TCanvas; DstX, DstY: Integer;
  682. SrcRect: TRect; Bitmap: TBitmap; TransparentColor: TColor);
  683. begin
  684. with SrcRect do
  685. StretchBitmapTransparent(Dest, Bitmap, TransparentColor,
  686. DstX, DstY, Right - Left, Bottom - Top, Left, Top, Right - Left,
  687. Bottom - Top);
  688. end;
  689. procedure DrawBitmapTransparent(Dest: TCanvas; DstX, DstY: Integer;
  690. Bitmap: TBitmap; TransparentColor: TColor);
  691. begin
  692. StretchBitmapTransparent(Dest, Bitmap, TransparentColor, DstX, DstY,
  693. Bitmap.Width, Bitmap.Height, 0, 0, Bitmap.Width, Bitmap.Height);
  694. end;
  695. { ChangeBitmapColor. This function create new TBitmap object.
  696. You must destroy it outside by calling TBitmap.Free method. }
  697. function ChangeBitmapColor(Bitmap: TBitmap; Color, NewColor: TColor): TBitmap;
  698. var
  699. R: TRect;
  700. begin
  701. Result := TBitmap.Create;
  702. try
  703. with Result do
  704. begin
  705. Height := Bitmap.Height;
  706. Width := Bitmap.Width;
  707. R := Bounds(0, 0, Width, Height);
  708. Canvas.Brush.Color := NewColor;
  709. Canvas.FillRect(R);
  710. Canvas.BrushCopy(R, Bitmap, R, Color);
  711. end;
  712. except
  713. Result.Free;
  714. raise;
  715. end;
  716. end;
  717. { CreateDisabledBitmap. Creating TBitmap object with disable button glyph
  718. image. You must destroy it outside by calling TBitmap.Free method. }
  719. const
  720. ROP_DSPDxax = $00E20746;
  721. function CreateDisabledBitmapEx(FOriginal: TBitmap; OutlineColor, BackColor,
  722. HighlightColor, ShadowColor: TColor; DrawHighlight: Boolean): TBitmap;
  723. var
  724. MonoBmp: TBitmap;
  725. IRect: TRect;
  726. begin
  727. IRect := Rect(0, 0, FOriginal.Width, FOriginal.Height);
  728. Result := TBitmap.Create;
  729. try
  730. Result.Width := FOriginal.Width;
  731. Result.Height := FOriginal.Height;
  732. MonoBmp := TBitmap.Create;
  733. try
  734. with MonoBmp do
  735. begin
  736. Width := FOriginal.Width;
  737. Height := FOriginal.Height;
  738. Canvas.CopyRect(IRect, FOriginal.Canvas, IRect);
  739. {$IFDEF RX_D3}
  740. HandleType := bmDDB;
  741. {$ENDIF}
  742. Canvas.Brush.Color := OutlineColor;
  743. if Monochrome then
  744. begin
  745. Canvas.Font.Color := clWhite;
  746. Monochrome := False;
  747. Canvas.Brush.Color := clWhite;
  748. end;
  749. Monochrome := True;
  750. end;
  751. with Result.Canvas do
  752. begin
  753. Brush.Color := BackColor;
  754. FillRect(IRect);
  755. if DrawHighlight then
  756. begin
  757. Brush.Color := HighlightColor;
  758. SetTextColor(Handle, clBlack);
  759. SetBkColor(Handle, clWhite);
  760. BitBlt(Handle, 1, 1, WidthOf(IRect), HeightOf(IRect),
  761. MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
  762. end;
  763. Brush.Color := ShadowColor;
  764. SetTextColor(Handle, clBlack);
  765. SetBkColor(Handle, clWhite);
  766. BitBlt(Handle, 0, 0, WidthOf(IRect), HeightOf(IRect),
  767. MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
  768. end;
  769. finally
  770. MonoBmp.Free;
  771. end;
  772. except
  773. Result.Free;
  774. raise;
  775. end;
  776. end;
  777. function CreateDisabledBitmap(FOriginal: TBitmap; OutlineColor: TColor): TBitmap;
  778. begin
  779. Result := CreateDisabledBitmapEx(FOriginal, OutlineColor,
  780. clBtnFace, clBtnHighlight, clBtnShadow, True);
  781. end;
  782. {$IFNDEF VER80}
  783. procedure ImageListDrawDisabled(Images: TImageList; Canvas: TCanvas;
  784. X, Y, Index: Integer; HighlightColor, GrayColor: TColor; DrawHighlight: Boolean);
  785. var
  786. Bmp: TBitmap;
  787. SaveColor: TColor;
  788. begin
  789. SaveColor := Canvas.Brush.Color;
  790. Bmp := TBitmap.Create;
  791. try
  792. Bmp.Width := Images.Width;
  793. Bmp.Height := Images.Height;
  794. with Bmp.Canvas do
  795. begin
  796. Brush.Color := clWhite;
  797. FillRect(Rect(0, 0, Images.Width, Images.Height));
  798. ImageList_Draw(Images.Handle, Index, Handle, 0, 0, ILD_MASK);
  799. end;
  800. Bmp.Monochrome := True;
  801. if DrawHighlight then
  802. begin
  803. Canvas.Brush.Color := HighlightColor;
  804. SetTextColor(Canvas.Handle, clWhite);
  805. SetBkColor(Canvas.Handle, clBlack);
  806. BitBlt(Canvas.Handle, X + 1, Y + 1, Images.Width,
  807. Images.Height, Bmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
  808. end;
  809. Canvas.Brush.Color := GrayColor;
  810. SetTextColor(Canvas.Handle, clWhite);
  811. SetBkColor(Canvas.Handle, clBlack);
  812. BitBlt(Canvas.Handle, X, Y, Images.Width,
  813. Images.Height, Bmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
  814. finally
  815. Bmp.Free;
  816. Canvas.Brush.Color := SaveColor;
  817. end;
  818. end;
  819. {$ENDIF}
  820. { Brush Pattern }
  821. function CreateTwoColorsBrushPattern(Color1, Color2: TColor): TBitmap;
  822. var
  823. X, Y: Integer;
  824. begin
  825. Result := TBitmap.Create;
  826. Result.Width := 8;
  827. Result.Height := 8;
  828. with Result.Canvas do
  829. begin
  830. Brush.Style := bsSolid;
  831. Brush.Color := Color1;
  832. FillRect(Rect(0, 0, Result.Width, Result.Height));
  833. for Y := 0 to 7 do
  834. for X := 0 to 7 do
  835. if (Y mod 2) = (X mod 2) then { toggles between even/odd pixles }
  836. Pixels[X, Y] := Color2; { on even/odd rows }
  837. end;
  838. end;
  839. { Icons }
  840. function MakeIcon(ResID: PChar): TIcon;
  841. begin
  842. Result := MakeModuleIcon(hInstance, ResID);
  843. end;
  844. function MakeIconID(ResID: Word): TIcon;
  845. begin
  846. Result := MakeModuleIcon(hInstance, MakeIntResource(ResID));
  847. end;
  848. function MakeModuleIcon(Module: THandle; ResID: PChar): TIcon;
  849. begin
  850. Result := TIcon.Create;
  851. Result.Handle := LoadIcon(Module, ResID);
  852. if Result.Handle = 0 then
  853. begin
  854. Result.Free;
  855. Result := nil;
  856. end;
  857. end;
  858. { Create TBitmap object from TIcon }
  859. function CreateBitmapFromIcon(Icon: TIcon; BackColor: TColor): TBitmap;
  860. var
  861. IWidth, IHeight: Integer;
  862. begin
  863. IWidth := Icon.Width;
  864. IHeight := Icon.Height;
  865. Result := TBitmap.Create;
  866. try
  867. Result.Width := IWidth;
  868. Result.Height := IHeight;
  869. with Result.Canvas do
  870. begin
  871. Brush.Color := BackColor;
  872. FillRect(Rect(0, 0, IWidth, IHeight));
  873. Draw(0, 0, Icon);
  874. end;
  875. {$IFDEF RX_D3}
  876. Result.TransparentColor := BackColor;
  877. Result.Transparent := True;
  878. {$ENDIF}
  879. except
  880. Result.Free;
  881. raise;
  882. end;
  883. end;
  884. {$IFNDEF VER80}
  885. function CreateIconFromBitmap(Bitmap: TBitmap; TransparentColor: TColor): TIcon;
  886. begin
  887. with TImageList.CreateSize(Bitmap.Width, Bitmap.Height) do
  888. try
  889. {$IFDEF RX_D3}
  890. if TransparentColor = clDefault then
  891. TransparentColor := Bitmap.TransparentColor;
  892. {$ENDIF}
  893. AllocBy := 1;
  894. AddMasked(Bitmap, TransparentColor);
  895. Result := TIcon.Create;
  896. try
  897. GetIcon(0, Result);
  898. except
  899. Result.Free;
  900. raise;
  901. end;
  902. finally
  903. Free;
  904. end;
  905. end;
  906. {$ENDIF}
  907. { Dialog units }
  908. function DialogUnitsToPixelsX(DlgUnits: Word): Word;
  909. begin
  910. Result := (DlgUnits * LoWord(GetDialogBaseUnits)) div 4;
  911. end;
  912. function DialogUnitsToPixelsY(DlgUnits: Word): Word;
  913. begin
  914. Result := (DlgUnits * HiWord(GetDialogBaseUnits)) div 8;
  915. end;
  916. function PixelsToDialogUnitsX(PixUnits: Word): Word;
  917. begin
  918. Result := PixUnits * 4 div LoWord(GetDialogBaseUnits);
  919. end;
  920. function PixelsToDialogUnitsY(PixUnits: Word): Word;
  921. begin
  922. Result := PixUnits * 8 div HiWord(GetDialogBaseUnits);
  923. end;
  924. { Service routines }
  925. type
  926. THack = class(TCustomControl);
  927. function LoadDLL(const LibName: string): THandle;
  928. var
  929. ErrMode: Cardinal;
  930. {$IFDEF VER80}
  931. P: array[0..255] of Char;
  932. {$ENDIF}
  933. begin
  934. ErrMode := SetErrorMode(SEM_NOOPENFILEERRORBOX);
  935. {$IFNDEF VER80}
  936. Result := LoadLibrary(PChar(LibName));
  937. {$ELSE}
  938. Result := LoadLibrary(StrPCopy(P, LibName));
  939. {$ENDIF}
  940. SetErrorMode(ErrMode);
  941. if Result < HINSTANCE_ERROR then
  942. {$IFNDEF VER80}
  943. {$IFDEF RX_D6} // Polaris
  944. RaiseLastOSError;
  945. {$ELSE}
  946. Win32Check(False);
  947. {$ENDIF}
  948. {$ELSE}
  949. raise EOutOfResources.CreateResFmt(SLoadLibError, [LibName]);
  950. {$ENDIF}
  951. end;
  952. function RegisterServer(const ModuleName: string): Boolean;
  953. { RegisterServer procedure written by Vladimir Gaitanoff, 2:50/430.2 }
  954. type
  955. TProc = procedure;
  956. var
  957. Handle: THandle;
  958. DllRegServ: Pointer;
  959. begin
  960. Result := False;
  961. Handle := LoadDLL(ModuleName);
  962. try
  963. DllRegServ := GetProcAddress(Handle, 'DllRegisterServer');
  964. if Assigned(DllRegServ) then
  965. begin
  966. TProc(DllRegServ);
  967. Result := True;
  968. end;
  969. finally
  970. FreeLibrary(Handle);
  971. end;
  972. end;
  973. procedure Beep;
  974. begin
  975. MessageBeep(0);
  976. end;
  977. procedure FreeUnusedOle;
  978. begin
  979. {$IFNDEF VER80}
  980. FreeLibrary(GetModuleHandle('OleAut32'));
  981. {$ENDIF}
  982. end;
  983. procedure NotImplemented;
  984. begin
  985. Screen.Cursor := crDefault;
  986. MessageDlg(RxLoadStr(SNotImplemented), mtInformation, [mbOk], 0);
  987. Abort;
  988. end;
  989. {$IFDEF VER80}
  990. procedure MoveWindowOrg(DC: HDC; DX, DY: Integer);
  991. var
  992. P: TPoint;
  993. begin
  994. GetWindowOrgEx(DC, @P);
  995. SetWindowOrgEx(DC, P.X - DX, P.Y - DY, nil);
  996. end;
  997. function IsLibrary: Boolean;
  998. begin
  999. Result := (PrefixSeg = 0);
  1000. end;
  1001. {$ENDIF}
  1002. procedure PaintInverseRect(const RectOrg, RectEnd: TPoint);
  1003. var
  1004. DC: HDC;
  1005. R: TRect;
  1006. begin
  1007. DC := GetDC(0);
  1008. try
  1009. R := Rect(RectOrg.X, RectOrg.Y, RectEnd.X, RectEnd.Y);
  1010. InvertRect(DC, R);
  1011. finally
  1012. ReleaseDC(0, DC);
  1013. end;
  1014. end;
  1015. procedure DrawInvertFrame(ScreenRect: TRect; Width: Integer);
  1016. var
  1017. DC: HDC;
  1018. I: Integer;
  1019. begin
  1020. DC := GetDC(0);
  1021. try
  1022. for I := 1 to Width do
  1023. begin
  1024. DrawFocusRect(DC, ScreenRect);
  1025. InflateRect(ScreenRect, -1, -1);
  1026. end;
  1027. finally
  1028. ReleaseDC(0, DC);
  1029. end;
  1030. end;
  1031. function WidthOf(R: TRect): Integer;
  1032. begin
  1033. Result := R.Right - R.Left;
  1034. end;
  1035. function HeightOf(R: TRect): Integer;
  1036. begin
  1037. Result := R.Bottom - R.Top;
  1038. end;
  1039. function PointInRect(const P: TPoint; const R: TRect): Boolean;
  1040. begin
  1041. with R do
  1042. Result := (Left <= P.X) and (Top <= P.Y) and
  1043. (Right >= P.X) and (Bottom >= P.Y);
  1044. end;
  1045. function PointInPolyRgn(const P: TPoint; const Points: array of TPoint): Boolean;
  1046. type
  1047. PPoints = ^TPoints;
  1048. TPoints = array[0..0] of TPoint;
  1049. var
  1050. Rgn: HRgn;
  1051. begin
  1052. Rgn := CreatePolygonRgn(PPoints(@Points)^, High(Points) + 1, WINDING);
  1053. try
  1054. Result := PtInRegion(Rgn, P.X, P.Y);
  1055. finally
  1056. DeleteObject(Rgn);
  1057. end;
  1058. end;
  1059. function PaletteColor(Color: TColor): Longint;
  1060. begin
  1061. Result := ColorToRGB(Color) or PaletteMask;
  1062. end;
  1063. procedure KillMessage(Wnd: HWnd; Msg: Cardinal);
  1064. { Delete the requested message from the queue, but throw back }
  1065. { any WM_QUIT msgs that PeekMessage may also return. }
  1066. { Copied from DbGrid.pas }
  1067. var
  1068. M: TMsg;
  1069. begin
  1070. M.Message := 0;
  1071. if PeekMessage(M, Wnd, Msg, Msg, PM_REMOVE) and (M.Message = WM_QUIT) then
  1072. PostQuitMessage(M.WParam);
  1073. end;
  1074. function CreateRotatedFont(Font: TFont; Angle: Integer): HFont;
  1075. var
  1076. LogFont: TLogFont;
  1077. begin
  1078. FillChar(LogFont, SizeOf(LogFont), 0);
  1079. with LogFont do begin
  1080. lfHeight := Font.Height;
  1081. lfWidth := 0;
  1082. lfEscapement := Angle * 10;
  1083. lfOrientation := 0;
  1084. if fsBold in Font.Style then lfWeight := FW_BOLD
  1085. else lfWeight := FW_NORMAL;
  1086. lfItalic := Ord(fsItalic in Font.Style);
  1087. lfUnderline := Ord(fsUnderline in Font.Style);
  1088. lfStrikeOut := Byte(fsStrikeOut in Font.Style);
  1089. {$IFDEF RX_D3}
  1090. lfCharSet := Byte(Font.Charset);
  1091. if AnsiCompareText(Font.Name, 'Default') = 0 then
  1092. StrPCopy(lfFaceName, string(DefFontData.Name))
  1093. else
  1094. StrPCopy(lfFaceName, Font.Name);
  1095. {$ELSE}
  1096. {$IFDEF VER93}
  1097. lfCharSet := Byte(Font.Charset);
  1098. {$ELSE}
  1099. lfCharSet := DEFAULT_CHARSET;
  1100. {$ENDIF}
  1101. StrPCopy(lfFaceName, Font.Name);
  1102. {$ENDIF}
  1103. lfQuality := DEFAULT_QUALITY;
  1104. lfOutPrecision := OUT_DEFAULT_PRECIS;
  1105. lfClipPrecision := CLIP_DEFAULT_PRECIS;
  1106. case Font.Pitch of
  1107. fpVariable: lfPitchAndFamily := VARIABLE_PITCH;
  1108. fpFixed: lfPitchAndFamily := FIXED_PITCH;
  1109. else lfPitchAndFamily := DEFAULT_PITCH;
  1110. end;
  1111. end;
  1112. Result := CreateFontIndirect(LogFont);
  1113. end;
  1114. procedure Delay(MSecs: Longint);
  1115. var
  1116. FirstTickCount, Now: Longint;
  1117. begin
  1118. FirstTickCount := GetTickCount;
  1119. repeat
  1120. Application.ProcessMessages;
  1121. { allowing access to other controls, etc. }
  1122. Now := GetTickCount;
  1123. until (Now - FirstTickCount >= MSecs) or (Now < FirstTickCount);
  1124. end;
  1125. function PaletteEntries(Palette: HPALETTE): Integer;
  1126. begin
  1127. GetObject(Palette, SizeOf(Integer), @Result);
  1128. end;
  1129. procedure CenterControl(Control: TControl);
  1130. var
  1131. X, Y: Integer;
  1132. begin
  1133. X := Control.Left;
  1134. Y := Control.Top;
  1135. if Control is TForm then
  1136. begin
  1137. with Control do
  1138. begin
  1139. if (TForm(Control).FormStyle = fsMDIChild) and (Application.MainForm <> nil) then
  1140. begin
  1141. X := (Application.MainForm.ClientWidth - Width) div 2;
  1142. Y := (Application.MainForm.ClientHeight - Height) div 2;
  1143. end
  1144. else
  1145. begin
  1146. X := (Screen.Width - Width) div 2;
  1147. Y := (Screen.Height - Height) div 2;
  1148. end;
  1149. end;
  1150. end
  1151. else if Control.Parent <> nil then
  1152. begin
  1153. with Control do
  1154. begin
  1155. Parent.HandleNeeded;
  1156. X := (Parent.ClientWidth - Width) div 2;
  1157. Y := (Parent.ClientHeight - Height) div 2;
  1158. end;
  1159. end;
  1160. if X < 0 then X := 0;
  1161. if Y < 0 then Y := 0;
  1162. with Control do SetBounds(X, Y, Width, Height);
  1163. end;
  1164. procedure FitRectToScreen(var Rect: TRect);
  1165. var
  1166. X, Y, Delta: Integer;
  1167. begin
  1168. X := GetSystemMetrics(SM_CXSCREEN);
  1169. Y := GetSystemMetrics(SM_CYSCREEN);
  1170. with Rect do
  1171. begin
  1172. if Right > X then
  1173. begin
  1174. Delta := Right - Left;
  1175. Right := X;
  1176. Left := Right - Delta;
  1177. end;
  1178. if Left < 0 then
  1179. begin
  1180. Delta := Right - Left;
  1181. Left := 0;
  1182. Right := Left + Delta;
  1183. end;
  1184. if Bottom > Y then
  1185. begin
  1186. Delta := Bottom - Top;
  1187. Bottom := Y;
  1188. Top := Bottom - Delta;
  1189. end;
  1190. if Top < 0 then
  1191. begin
  1192. Delta := Bottom - Top;
  1193. Top := 0;
  1194. Bottom := Top + Delta;
  1195. end;
  1196. end;
  1197. end;
  1198. procedure CenterWindow(Wnd: HWnd);
  1199. var
  1200. R: TRect;
  1201. begin
  1202. GetWindowRect(Wnd, R);
  1203. R := Rect((GetSystemMetrics(SM_CXSCREEN) - R.Right + R.Left) div 2,
  1204. (GetSystemMetrics(SM_CYSCREEN) - R.Bottom + R.Top) div 2,
  1205. R.Right - R.Left, R.Bottom - R.Top);
  1206. FitRectToScreen(R);
  1207. SetWindowPos(Wnd, 0, R.Left, R.Top, 0, 0, SWP_NOACTIVATE or
  1208. SWP_NOSIZE or SWP_NOZORDER);
  1209. end;
  1210. procedure MergeForm(AControl: TWinControl; AForm: TForm; Align: TAlign;
  1211. Show: Boolean);
  1212. var
  1213. R: TRect;
  1214. AutoScroll: Boolean;
  1215. begin
  1216. AutoScroll := AForm.AutoScroll;
  1217. AForm.Hide;
  1218. THack(AForm).DestroyHandle;
  1219. with AForm do
  1220. begin
  1221. BorderStyle := bsNone;
  1222. BorderIcons := [];
  1223. Parent := AControl;
  1224. end;
  1225. AControl.DisableAlign;
  1226. try
  1227. if Align <> alNone then AForm.Align := Align
  1228. else
  1229. begin
  1230. R := AControl.ClientRect;
  1231. AForm.SetBounds(R.Left + AForm.Left, R.Top + AForm.Top, AForm.Width,
  1232. AForm.Height);
  1233. end;
  1234. AForm.AutoScroll := AutoScroll;
  1235. AForm.Visible := Show;
  1236. finally
  1237. AControl.EnableAlign;
  1238. end;
  1239. end;
  1240. {$IFNDEF VER80}
  1241. { ShowMDIClientEdge function has been copied from Inprise's FORMS.PAS unit,
  1242. Delphi 4 version }
  1243. procedure ShowMDIClientEdge(ClientHandle: THandle; ShowEdge: Boolean);
  1244. var
  1245. Style: Longint;
  1246. begin
  1247. if ClientHandle <> 0 then
  1248. begin
  1249. Style := GetWindowLong(ClientHandle, GWL_EXSTYLE);
  1250. if ShowEdge then
  1251. if Style and WS_EX_CLIENTEDGE = 0 then
  1252. Style := Style or WS_EX_CLIENTEDGE
  1253. else
  1254. Exit
  1255. else if Style and WS_EX_CLIENTEDGE <> 0 then
  1256. Style := Style and not WS_EX_CLIENTEDGE
  1257. else
  1258. Exit;
  1259. SetWindowLong(ClientHandle, GWL_EXSTYLE, Style);
  1260. SetWindowPos(ClientHandle, 0, 0,0,0,0, SWP_FRAMECHANGED or SWP_NOACTIVATE or
  1261. SWP_NOMOVE or SWP_NOSIZE or SWP_NOZORDER);
  1262. end;
  1263. end;
  1264. function MakeVariant(const Values: array of Variant): Variant;
  1265. begin
  1266. if High(Values) - Low(Values) > 1 then
  1267. Result := VarArrayOf(Values)
  1268. else if High(Values) - Low(Values) = 1 then
  1269. Result := Values[Low(Values)]
  1270. else Result := Null;
  1271. end;
  1272. {$ENDIF}
  1273. { Shade rectangle }
  1274. procedure ShadeRect(DC: HDC; const Rect: TRect);
  1275. const
  1276. HatchBits: array[0..7] of Word = ($11, $22, $44, $88, $11, $22, $44, $88);
  1277. var
  1278. Bitmap: HBitmap;
  1279. SaveBrush: HBrush;
  1280. SaveTextColor, SaveBkColor: TColorRef;
  1281. begin
  1282. Bitmap := CreateBitmap(8, 8, 1, 1, @HatchBits);
  1283. SaveBrush := SelectObject(DC, CreatePatternBrush(Bitmap));
  1284. try
  1285. SaveTextColor := SetTextColor(DC, clWhite);
  1286. SaveBkColor := SetBkColor(DC, clBlack);
  1287. with Rect do PatBlt(DC, Left, Top, Right - Left, Bottom - Top, $00A000C9);
  1288. SetBkColor(DC, SaveBkColor);
  1289. SetTextColor(DC, SaveTextColor);
  1290. finally
  1291. DeleteObject(SelectObject(DC, SaveBrush));
  1292. DeleteObject(Bitmap);
  1293. end;
  1294. end;
  1295. function ScreenWorkArea: TRect;
  1296. {$IFDEF VER80}
  1297. const
  1298. SPI_GETWORKAREA = 48;
  1299. {$ENDIF}
  1300. begin
  1301. if not SystemParametersInfo(SPI_GETWORKAREA, 0, @Result, 0) then
  1302. with Screen do Result := Bounds(0, 0, Width, Height);
  1303. end;
  1304. function WindowClassName(Wnd: HWnd): string;
  1305. var
  1306. Buffer: array[0..255] of Char;
  1307. begin
  1308. SetString(Result, Buffer, GetClassName(Wnd, Buffer, SizeOf(Buffer) - 1));
  1309. end;
  1310. {$IFNDEF VER80}
  1311. function GetAnimation: Boolean;
  1312. var
  1313. Info: TAnimationInfo;
  1314. begin
  1315. Info.cbSize := SizeOf(TAnimationInfo);
  1316. if SystemParametersInfo(SPI_GETANIMATION, SizeOf(Info), @Info, 0) then
  1317. {$IFDEF RX_D3}
  1318. Result := Info.iMinAnimate <> 0
  1319. {$ELSE}
  1320. Result := Info.iMinAnimate
  1321. {$ENDIF}
  1322. else Result := False;
  1323. end;
  1324. procedure SetAnimation(Value: Boolean);
  1325. var
  1326. Info: TAnimationInfo;
  1327. begin
  1328. Info.cbSize := SizeOf(TAnimationInfo);
  1329. BOOL(Info.iMinAnimate) := Value;
  1330. SystemParametersInfo(SPI_SETANIMATION, SizeOf(Info), @Info, 0);
  1331. end;
  1332. procedure ShowWinNoAnimate(Handle: HWnd; CmdShow: Integer);
  1333. var
  1334. Animation: Boolean;
  1335. begin
  1336. Animation := GetAnimation;
  1337. if Animation then SetAnimation(False);
  1338. ShowWindow(Handle, CmdShow);
  1339. if Animation then SetAnimation(True);
  1340. end;
  1341. {$ELSE}
  1342. procedure ShowWinNoAnimate(Handle: HWnd; CmdShow: Integer);
  1343. begin
  1344. ShowWindow(Handle, CmdShow);
  1345. end;
  1346. procedure SwitchToThisWindow(Wnd: HWnd; Restore: Bool); far; external 'USER'
  1347. index 172;
  1348. {$ENDIF}
  1349. procedure SwitchToWindow(Wnd: HWnd; Restore: Boolean);
  1350. begin
  1351. if IsWindowEnabled(Wnd) then
  1352. begin
  1353. {$IFNDEF VER80}
  1354. SetForegroundWindow(Wnd);
  1355. if Restore and IsWindowVisible(Wnd) then
  1356. begin
  1357. if not IsZoomed(Wnd) then
  1358. SendMessage(Wnd, WM_SYSCOMMAND, SC_RESTORE, 0);
  1359. SetFocus(Wnd);
  1360. end;
  1361. {$ELSE}
  1362. SwitchToThisWindow(Wnd, Restore);
  1363. {$ENDIF}
  1364. end;
  1365. end;
  1366. function GetWindowParent(Wnd: HWnd): HWnd;
  1367. begin
  1368. {$IFNDEF VER80}
  1369. Result := GetWindowLong(Wnd, GWL_HWNDPARENT);
  1370. {$ELSE}
  1371. Result := GetWindowWord(Wnd, GWW_HWNDPARENT);
  1372. {$ENDIF}
  1373. end;
  1374. procedure ActivateWindow(Wnd: HWnd);
  1375. begin
  1376. if Wnd <> 0 then
  1377. begin
  1378. ShowWinNoAnimate(Wnd, SW_SHOW);
  1379. {$IFNDEF VER80}
  1380. SetForegroundWindow(Wnd);
  1381. {$ELSE}
  1382. SwitchToThisWindow(Wnd, True);
  1383. {$ENDIF}
  1384. end;
  1385. end;
  1386. {$IFDEF CBUILDER}
  1387. function FindPrevInstance(const MainFormClass: ShortString;
  1388. const ATitle: string): HWnd;
  1389. {$ELSE}
  1390. function FindPrevInstance(const MainFormClass, ATitle: string): HWnd;
  1391. {$ENDIF CBUILDER}
  1392. var
  1393. BufClass, BufTitle: PChar;
  1394. begin
  1395. Result := 0;
  1396. if (MainFormClass = '') and (ATitle = '') then Exit;
  1397. BufClass := nil; BufTitle := nil;
  1398. if (MainFormClass <> '') then BufClass := StrPAlloc(MainFormClass);
  1399. if (ATitle <> '') then BufTitle := StrPAlloc(ATitle);
  1400. try
  1401. Result := FindWindow(BufClass, BufTitle);
  1402. finally
  1403. StrDispose(BufTitle);
  1404. StrDispose(BufClass);
  1405. end;
  1406. end;
  1407. {$IFNDEF VER80}
  1408. function WindowsEnum(Handle: HWnd; Param: Longint): Bool; export; stdcall;
  1409. begin
  1410. if WindowClassName(Handle) = 'TAppBuilder' then
  1411. begin
  1412. Result := False;
  1413. PLongint(Param)^ := 1;
  1414. end
  1415. else Result := True;
  1416. end;
  1417. {$ENDIF}
  1418. {$IFDEF CBUILDER}
  1419. function ActivatePrevInstance(const MainFormClass: ShortString;
  1420. const ATitle: string): Boolean;
  1421. {$ELSE}
  1422. function ActivatePrevInstance(const MainFormClass, ATitle: string): Boolean;
  1423. {$ENDIF CBUILDER}
  1424. var
  1425. PrevWnd, PopupWnd, ParentWnd: HWnd;
  1426. {$IFNDEF VER80}
  1427. IsDelphi: Longint;
  1428. {$ELSE}
  1429. S: array[0..255] of Char;
  1430. {$ENDIF}
  1431. begin
  1432. Result := False;
  1433. PrevWnd := FindPrevInstance(MainFormClass, ATitle);
  1434. if PrevWnd <> 0 then
  1435. begin
  1436. ParentWnd := GetWindowParent(PrevWnd);
  1437. while (ParentWnd <> GetDesktopWindow) and (ParentWnd <> 0) do
  1438. begin
  1439. PrevWnd := ParentWnd;
  1440. ParentWnd := GetWindowParent(PrevWnd);
  1441. end;
  1442. if WindowClassName(PrevWnd) = 'TApplication' then
  1443. begin
  1444. {$IFNDEF VER80}
  1445. IsDelphi := 0;
  1446. EnumThreadWindows(GetWindowTask(PrevWnd), @WindowsEnum, LPARAM(@IsDelphi));
  1447. if Boolean(IsDelphi) then Exit;
  1448. {$ELSE}
  1449. GetModuleFileName(GetWindowTask(PrevWnd), S, SizeOf(S) - 1); //non unicode
  1450. if AnsiUpperCase(ExtractFileName(StrPas(S))) = 'DELPHI.EXE' then Exit;
  1451. {$ENDIF}
  1452. if IsIconic(PrevWnd) then { application is minimized }
  1453. begin
  1454. SendMessage(PrevWnd, WM_SYSCOMMAND, SC_RESTORE, 0);
  1455. Result := True;
  1456. Exit;
  1457. end
  1458. else ShowWinNoAnimate(PrevWnd, SW_SHOWNOACTIVATE);
  1459. end
  1460. else ActivateWindow(PrevWnd);
  1461. PopupWnd := GetLastActivePopup(PrevWnd);
  1462. if (PrevWnd <> PopupWnd) and IsWindowVisible(PopupWnd) and IsWindowEnabled(PopupWnd) then
  1463. begin
  1464. {$IFNDEF VER80}
  1465. SetForegroundWindow(PopupWnd);
  1466. {$ELSE}
  1467. BringWindowToTop(PopupWnd);
  1468. {$ENDIF}
  1469. end
  1470. else ActivateWindow(PopupWnd);
  1471. Result := True;
  1472. end;
  1473. end;
  1474. { Standard Windows MessageBox function }
  1475. function MsgBox(const Caption, Text: string; Flags: Integer): Integer;
  1476. {$IFNDEF VER80}
  1477. begin
  1478. {$IFNDEF RX_D5}
  1479. SetAutoSubClass(True);
  1480. try
  1481. {$ENDIF}
  1482. Result := Application.MessageBox(PChar(Text), PChar(Caption), Flags);
  1483. {$IFNDEF RX_D5}
  1484. finally
  1485. SetAutoSubClass(False);
  1486. end;
  1487. {$ENDIF}
  1488. end;
  1489. {$ELSE}
  1490. var
  1491. BufMsg, BufCaption: PChar;
  1492. begin
  1493. SetAutoSubClass(True);
  1494. BufMsg := StrPAlloc(Text);
  1495. BufCaption := StrPAlloc(Caption);
  1496. try
  1497. Result := Application.MessageBox(BufMsg, BufCaption, Flags);
  1498. finally
  1499. StrDispose(BufCaption);
  1500. StrDispose(BufMsg);
  1501. SetAutoSubClass(False);
  1502. end;
  1503. end;
  1504. {$ENDIF}
  1505. function MsgDlg(const Msg: string; AType: TMsgDlgType;
  1506. AButtons: TMsgDlgButtons; HelpCtx: Longint): Word;
  1507. {$IFNDEF VER80}
  1508. begin
  1509. Result := MessageDlg(Msg, AType, AButtons, HelpCtx);
  1510. end;
  1511. {$ELSE}
  1512. var
  1513. KeepGlyphs: Boolean;
  1514. KeepSize: TPoint;
  1515. begin
  1516. if NewStyleControls then
  1517. begin
  1518. KeepGlyphs := MsgDlgGlyphs;
  1519. KeepSize := MsgDlgBtnSize;
  1520. MsgDlgBtnSize := Point(77, 25);
  1521. MsgDlgGlyphs := False;
  1522. end;
  1523. try
  1524. Result := MessageDlg(Msg, AType, AButtons, HelpCtx);
  1525. finally
  1526. if NewStyleControls then
  1527. begin
  1528. MsgDlgBtnSize := KeepSize;
  1529. MsgDlgGlyphs := KeepGlyphs;
  1530. end;
  1531. end;
  1532. end;
  1533. {$ENDIF}
  1534. { Gradient fill procedure - displays a gradient beginning with a chosen }
  1535. { color and ending with another chosen color. Based on TGradientFill }
  1536. { component source code written by Curtis White, cwhite@teleport.com. }
  1537. procedure GradientFillRect(Canvas: TCanvas; ARect: TRect; StartColor,
  1538. EndColor: TColor; Direction: TFillDirection; Colors: Byte);
  1539. var
  1540. StartRGB: array[0..2] of Byte; { Start RGB values }
  1541. RGBDelta: array[0..2] of Integer; { Difference between start and end RGB values }
  1542. ColorBand: TRect; { Color band rectangular coordinates }
  1543. I, Delta: Integer;
  1544. Brush: HBrush;
  1545. begin
  1546. if IsRectEmpty(ARect) then Exit;
  1547. if Colors < 2 then
  1548. begin
  1549. Brush := CreateSolidBrush(ColorToRGB(StartColor));
  1550. FillRect(Canvas.Handle, ARect, Brush);
  1551. DeleteObject(Brush);
  1552. Exit;
  1553. end;
  1554. StartColor := ColorToRGB(StartColor);
  1555. EndColor := ColorToRGB(EndColor);
  1556. case Direction of
  1557. fdTopToBottom, fdLeftToRight:
  1558. begin
  1559. { Set the Red, Green and Blue colors }
  1560. StartRGB[0] := GetRValue(StartColor);
  1561. StartRGB[1] := GetGValue(StartColor);
  1562. StartRGB[2] := GetBValue(StartColor);
  1563. { Calculate the difference between begin and end RGB values }
  1564. RGBDelta[0] := GetRValue(EndColor) - StartRGB[0];
  1565. RGBDelta[1] := GetGValue(EndColor) - StartRGB[1];
  1566. RGBDelta[2] := GetBValue(EndColor) - StartRGB[2];
  1567. end;
  1568. fdBottomToTop, fdRightToLeft:
  1569. begin
  1570. { Set the Red, Green and Blue colors }
  1571. { Reverse of TopToBottom and LeftToRight directions }
  1572. StartRGB[0] := GetRValue(EndColor);
  1573. StartRGB[1] := GetGValue(EndColor);
  1574. StartRGB[2] := GetBValue(EndColor);
  1575. { Calculate the difference between begin and end RGB values }
  1576. { Reverse of TopToBottom and LeftToRight directions }
  1577. RGBDelta[0] := GetRValue(StartColor) - StartRGB[0];
  1578. RGBDelta[1] := GetGValue(StartColor) - StartRGB[1];
  1579. RGBDelta[2] := GetBValue(StartColor) - StartRGB[2];
  1580. end;
  1581. end; {case}
  1582. { Calculate the color band's coordinates }
  1583. ColorBand := ARect;
  1584. if Direction in [fdTopToBottom, fdBottomToTop] then
  1585. begin
  1586. Colors := Max(2, Min(Colors, HeightOf(ARect)));
  1587. Delta := HeightOf(ARect) div Colors;
  1588. end
  1589. else
  1590. begin
  1591. Colors := Max(2, Min(Colors, WidthOf(ARect)));
  1592. Delta := WidthOf(ARect) div Colors;
  1593. end;
  1594. with Canvas.Pen do
  1595. begin { Set the pen style and mode }
  1596. Style := psSolid;
  1597. Mode := pmCopy;
  1598. end;
  1599. { Perform the fill }
  1600. if Delta > 0 then
  1601. begin
  1602. for I := 0 to Colors do
  1603. begin
  1604. case Direction of
  1605. { Calculate the color band's top and bottom coordinates }
  1606. fdTopToBottom, fdBottomToTop:
  1607. begin
  1608. ColorBand.Top := ARect.Top + I * Delta;
  1609. ColorBand.Bottom := ColorBand.Top + Delta;
  1610. end;
  1611. { Calculate the color band's left and right coordinates }
  1612. fdLeftToRight, fdRightToLeft:
  1613. begin
  1614. ColorBand.Left := ARect.Left + I * Delta;
  1615. ColorBand.Right := ColorBand.Left + Delta;
  1616. end;
  1617. end; {case}
  1618. { Calculate the color band's color }
  1619. Brush := CreateSolidBrush(RGB(
  1620. StartRGB[0] + MulDiv(I, RGBDelta[0], Colors - 1),
  1621. StartRGB[1] + MulDiv(I, RGBDelta[1], Colors - 1),
  1622. StartRGB[2] + MulDiv(I, RGBDelta[2], Colors - 1)));
  1623. FillRect(Canvas.Handle, ColorBand, Brush);
  1624. DeleteObject(Brush);
  1625. end;
  1626. end;
  1627. if Direction in [fdTopToBottom, fdBottomToTop] then
  1628. Delta := HeightOf(ARect) mod Colors
  1629. else Delta := WidthOf(ARect) mod Colors;
  1630. if Delta > 0 then
  1631. begin
  1632. case Direction of
  1633. { Calculate the color band's top and bottom coordinates }
  1634. fdTopToBottom, fdBottomToTop:
  1635. begin
  1636. ColorBand.Top := ARect.Bottom - Delta;
  1637. ColorBand.Bottom := ColorBand.Top + Delta;
  1638. end;
  1639. { Calculate the color band's left and right coordinates }
  1640. fdLeftToRight, fdRightToLeft:
  1641. begin
  1642. ColorBand.Left := ARect.Right - Delta;
  1643. ColorBand.Right := ColorBand.Left + Delta;
  1644. end;
  1645. end; {case}
  1646. case Direction of
  1647. fdTopToBottom, fdLeftToRight:
  1648. Brush := CreateSolidBrush(EndColor);
  1649. else {fdBottomToTop, fdRightToLeft }
  1650. Brush := CreateSolidBrush(StartColor);
  1651. end;
  1652. FillRect(Canvas.Handle, ColorBand, Brush);
  1653. DeleteObject(Brush);
  1654. end;
  1655. end;
  1656. function MinimizeText(const Text: string; Canvas: TCanvas;
  1657. MaxWidth: Integer): string;
  1658. var
  1659. I: Integer;
  1660. begin
  1661. Result := Text;
  1662. I := 1;
  1663. while (I <= Length(Text)) and (Canvas.TextWidth(Result) > MaxWidth) do
  1664. begin
  1665. Inc(I);
  1666. Result := Copy(Text, 1, Max(0, Length(Text) - I)) + '...';
  1667. end;
  1668. end;
  1669. function GetAveCharSize(Canvas: TCanvas): TPoint;
  1670. var
  1671. I: Integer;
  1672. Buffer: array[0..51] of Char;
  1673. begin
  1674. for I := 0 to 25 do Buffer[I] := Chr(I + Ord('A'));
  1675. for I := 0 to 25 do Buffer[I + 26] := Chr(I + Ord('a'));
  1676. GetTextExtentPoint(Canvas.Handle, Buffer, 52, TSize(Result));
  1677. Result.X := Result.X div 52;
  1678. end;
  1679. { Memory routines }
  1680. function AllocMemo(Size: Longint): Pointer;
  1681. begin
  1682. if Size > 0 then
  1683. Result := GlobalAllocPtr(HeapAllocFlags or GMEM_ZEROINIT, Size)
  1684. else Result := nil;
  1685. end;
  1686. function ReallocMemo(fpBlock: Pointer; Size: Longint): Pointer;
  1687. begin
  1688. Result := GlobalReallocPtr(fpBlock, Size,
  1689. HeapAllocFlags or GMEM_ZEROINIT);
  1690. end;
  1691. procedure FreeMemo(var fpBlock: Pointer);
  1692. begin
  1693. if fpBlock <> nil then
  1694. begin
  1695. GlobalFreePtr(fpBlock);
  1696. fpBlock := nil;
  1697. end;
  1698. end;
  1699. function GetMemoSize(fpBlock: Pointer): Longint;
  1700. var
  1701. hMem: THandle;
  1702. begin
  1703. Result := 0;
  1704. if fpBlock <> nil then
  1705. begin
  1706. {$IFNDEF VER80}
  1707. hMem := GlobalHandle(fpBlock);
  1708. {$ELSE}
  1709. hMem := LoWord(GlobalHandle(SelectorOf(fpBlock)));
  1710. {$ENDIF}
  1711. if hMem <> 0 then Result := GlobalSize(hMem);
  1712. end;
  1713. end;
  1714. {$IFDEF WIN64}
  1715. function CompareMem(fpBlock1, fpBlock2: Pointer; Size: UINT): Boolean; assembler;
  1716. begin
  1717. Result := Sysutils.CompareMem(fpBlock1, fpBlock2, Size);
  1718. end;
  1719. {$ELSE}
  1720. function CompareMem(fpBlock1, fpBlock2: Pointer; Size: Cardinal): Boolean; assembler;
  1721. asm
  1722. {$IFNDEF VER80}
  1723. PUSH ESI
  1724. PUSH EDI
  1725. MOV ESI,fpBlock1
  1726. MOV EDI,fpBlock2
  1727. MOV ECX,Size
  1728. MOV EDX,ECX
  1729. XOR EAX,EAX
  1730. AND EDX,3
  1731. SHR ECX,2
  1732. REPE CMPSD
  1733. JNE @@2
  1734. MOV ECX,EDX
  1735. REPE CMPSB
  1736. JNE @@2
  1737. @@1: INC EAX
  1738. @@2: POP EDI
  1739. POP ESI
  1740. {$ELSE}
  1741. PUSH DS
  1742. LDS SI,fpBlock1
  1743. LES DI,fpBlock2
  1744. MOV CX,Size
  1745. XOR AX,AX
  1746. CLD
  1747. REPE CMPSB
  1748. JNE @@1
  1749. INC AX
  1750. @@1: POP DS
  1751. {$ENDIF}
  1752. end;
  1753. {$ENDIF}
  1754. {$IFNDEF RX_D5}
  1755. procedure FreeAndNil(var Obj);
  1756. var
  1757. P: TObject;
  1758. begin
  1759. P := TObject(Obj);
  1760. TObject(Obj) := nil;
  1761. P.Free;
  1762. end;
  1763. {$ENDIF}
  1764. { Manipulate huge pointers routines by Ray Lischner, The Waite Group, Inc. }
  1765. {$IFNDEF VER80}
  1766. procedure HugeInc(var HugePtr: Pointer; Amount: Longint);
  1767. begin
  1768. HugePtr := PAnsiChar(HugePtr) + Amount;
  1769. end;
  1770. procedure HugeDec(var HugePtr: Pointer; Amount: Longint);
  1771. begin
  1772. HugePtr := PAnsiChar(HugePtr) - Amount;
  1773. end;
  1774. function HugeOffset(HugePtr: Pointer; Amount: Longint): Pointer;
  1775. begin
  1776. Result := PAnsiChar(HugePtr) + Amount;
  1777. end;
  1778. procedure HMemCpy(DstPtr, SrcPtr: Pointer; Amount: Longint);
  1779. begin
  1780. Move(SrcPtr^, DstPtr^, Amount);
  1781. end;
  1782. procedure HugeMove(Base: Pointer; Dst, Src, Size: Longint);
  1783. var
  1784. SrcPtr, DstPtr: PAnsiChar;
  1785. begin
  1786. SrcPtr := PAnsiChar(Base) + Src * SizeOf(Pointer);
  1787. DstPtr := PAnsiChar(Base) + Dst * SizeOf(Pointer);
  1788. Move(SrcPtr^, DstPtr^, Size * SizeOf(Pointer));
  1789. end;
  1790. {$ELSE}
  1791. procedure __AHSHIFT; far; external 'KERNEL' index 113;
  1792. { Increment a huge pointer }
  1793. procedure HugeInc(var HugePtr: Pointer; Amount: Longint); assembler;
  1794. asm
  1795. MOV AX,Amount.Word[0]
  1796. MOV DX,Amount.Word[2]
  1797. LES BX,HugePtr
  1798. ADD AX,ES:[BX]
  1799. ADC DX,0
  1800. MOV CX,Offset __AHSHIFT
  1801. SHL DX,CL
  1802. ADD ES:[BX+2],DX
  1803. MOV ES:[BX],AX
  1804. end;
  1805. { Decrement a huge pointer }
  1806. procedure HugeDec(var HugePtr: Pointer; Amount: Longint); assembler;
  1807. asm
  1808. LES BX,HugePtr
  1809. MOV AX,ES:[BX]
  1810. SUB AX,Amount.Word[0]
  1811. MOV DX,Amount.Word[2]
  1812. ADC DX,0
  1813. MOV CX,OFFSET __AHSHIFT
  1814. SHL DX,CL
  1815. SUB ES:[BX+2],DX
  1816. MOV ES:[BX],AX
  1817. end;
  1818. { ADD an offset to a huge pointer and return the result }
  1819. function HugeOffset(HugePtr: Pointer; Amount: Longint): Pointer; assembler;
  1820. asm
  1821. MOV AX,Amount.Word[0]
  1822. MOV DX,Amount.Word[2]
  1823. ADD AX,HugePtr.Word[0]
  1824. ADC DX,0
  1825. MOV CX,OFFSET __AHSHIFT
  1826. SHL DX,CL
  1827. ADD DX,HugePtr.Word[2]
  1828. end;
  1829. { When setting the Count, one might add many new items, which
  1830. must be set to zero at one time, to initialize all items to nil.
  1831. You could use FillChar, which fills by bytes, but, as DoMove
  1832. is to Move, ZeroBytes is to FillChar, except that it always
  1833. fill with zero valued words }
  1834. procedure FillWords(DstPtr: Pointer; Size: Word; Fill: Word); assembler;
  1835. asm
  1836. MOV AX,Fill
  1837. LES DI,DstPtr
  1838. MOV CX,Size.Word[0]
  1839. CLD
  1840. REP STOSW
  1841. end;
  1842. { Fill Length bytes of memory with Fill, starting at Ptr.
  1843. This is just like the procedure in the Win32 API. The memory
  1844. can be larger than 64K and can cross segment boundaries }
  1845. procedure FillMemory(Ptr: Pointer; Length: Longint; Fill: Byte);
  1846. var
  1847. NBytes: Cardinal;
  1848. NWords: Cardinal;
  1849. FillWord: Word;
  1850. begin
  1851. WordRec(FillWord).Hi := Fill;
  1852. WordRec(FillWord).Lo := Fill;
  1853. while Length > 1 do
  1854. begin
  1855. { Determine the number of bytes remaining in the segment }
  1856. if Ofs(Ptr^) = 0 then NBytes := $FFFE
  1857. else NBytes := $10000 - Ofs(Ptr^);
  1858. if NBytes > Length then NBytes := Length;
  1859. { Filling by words is faster than filling by bytes }
  1860. NWords := NBytes div 2;
  1861. FillWords(Ptr, NWords, FillWord);
  1862. NBytes := NWords * 2;
  1863. Dec(Length, NBytes);
  1864. Ptr := HugeOffset(Ptr, NBytes);
  1865. end;
  1866. { If the fill size is odd, then fill the remaining byte }
  1867. if Length > 0 then PByte(Ptr)^ := Fill;
  1868. end;
  1869. procedure ZeroMemory(Ptr: Pointer; Length: Longint);
  1870. begin
  1871. FillMemory(Ptr, Length, 0);
  1872. end;
  1873. procedure cld; inline ($FC);
  1874. procedure std; inline ($FD);
  1875. function ComputeDownMoveSize(SrcOffset, DstOffset: Word): Word;
  1876. begin
  1877. if SrcOffset > DstOffset then Result := Word($10000 - SrcOffset) div 2
  1878. else Result := Word($10000 - DstOffset) div 2;
  1879. if Result = 0 then Result := $7FFF;
  1880. end;
  1881. function ComputeUpMoveSize(SrcOffset, DstOffset: Word): Word;
  1882. begin
  1883. if SrcOffset = $FFFF then Result := DstOffset div 2
  1884. else if DstOffset = $FFFF then Result := SrcOffset div 2
  1885. else if SrcOffset > DstOffset then Result := DstOffset div 2 + 1
  1886. else Result := SrcOffset div 2 + 1;
  1887. end;
  1888. procedure MoveWords(SrcPtr, DstPtr: Pointer; Size: Word); assembler;
  1889. asm
  1890. PUSH DS
  1891. LDS SI,SrcPtr
  1892. LES DI,DstPtr
  1893. MOV CX,Size.Word[0]
  1894. REP MOVSW
  1895. POP DS
  1896. end;
  1897. procedure HugeMove(Base: Pointer; Dst, Src, Size: Longint);
  1898. var
  1899. SrcPtr, DstPtr: Pointer;
  1900. MoveSize: Word;
  1901. begin
  1902. SrcPtr := HugeOffset(Base, Src * SizeOf(Pointer));
  1903. DstPtr := HugeOffset(Base, Dst * SizeOf(Pointer));
  1904. { Convert longword size to words }
  1905. Size := Size * (SizeOf(Longint) div SizeOf(Word));
  1906. if Src < Dst then
  1907. begin
  1908. { Start from the far end and work toward the front }
  1909. std;
  1910. HugeInc(SrcPtr, (Size - 1) * SizeOf(Word));
  1911. HugeInc(DstPtr, (Size - 1) * SizeOf(Word));
  1912. while Size > 0 do
  1913. begin
  1914. { Compute how many bytes to move in the current segment }
  1915. MoveSize := ComputeUpMoveSize(Word(SrcPtr), Word(DstPtr));
  1916. if MoveSize > Size then MoveSize := Word(Size);
  1917. { Move the bytes }
  1918. MoveWords(SrcPtr, DstPtr, MoveSize);
  1919. { Update the number of bytes left to move }
  1920. Dec(Size, MoveSize);
  1921. { Update the pointers }
  1922. HugeDec(SrcPtr, MoveSize * SizeOf(Word));
  1923. HugeDec(DstPtr, MoveSize * SizeOf(Word));
  1924. end;
  1925. cld; { reset the direction flag }
  1926. end
  1927. else
  1928. begin
  1929. { Start from the beginning and work toward the end }
  1930. cld;
  1931. while Size > 0 do
  1932. begin
  1933. { Compute how many bytes to move in the current segment }
  1934. MoveSize := ComputeDownMoveSize(Word(SrcPtr), Word(DstPtr));
  1935. if MoveSize > Size then MoveSize := Word(Size);
  1936. { Move the bytes }
  1937. MoveWords(SrcPtr, DstPtr, MoveSize);
  1938. { Update the number of bytes left to move }
  1939. Dec(Size, MoveSize);
  1940. { Advance the pointers }
  1941. HugeInc(SrcPtr, MoveSize * SizeOf(Word));
  1942. HugeInc(DstPtr, MoveSize * SizeOf(Word));
  1943. end;
  1944. end;
  1945. end;
  1946. {$ENDIF}
  1947. { String routines }
  1948. {$W+}
  1949. function GetEnvVar(const VarName: string): string;
  1950. var
  1951. {$IFNDEF VER80}
  1952. S: array[0..2048] of Char;
  1953. {$ELSE}
  1954. S: array[0..255] of Char;
  1955. L: Cardinal;
  1956. P: PChar;
  1957. {$ENDIF}
  1958. begin
  1959. {$IFNDEF VER80}
  1960. if GetEnvironmentVariable(PChar(VarName), S, SizeOf(S) - 1) > 0 then
  1961. Result := StrPas(S)
  1962. else Result := '';
  1963. {$ELSE}
  1964. L := Length(VarName);
  1965. P := GetDosEnvironment;
  1966. StrPLCopy(S, VarName, 255);
  1967. while P^ <> #0 do begin
  1968. if (StrLIComp(P, {$IFNDEF VER80} PChar(VarName) {$ELSE} S {$ENDIF}, L) = 0) and
  1969. (P[L] = '=') then
  1970. begin
  1971. Result := StrPas(P + L + 1);
  1972. Exit;
  1973. end;
  1974. Inc(P, StrLen(P) + 1);
  1975. end;
  1976. Result := '';
  1977. {$ENDIF}
  1978. end;
  1979. {$W-}
  1980. { function GetParamStr copied from SYSTEM.PAS unit of Delphi 2.0 }
  1981. function GetParamStr(P: PChar; var Param: string): PChar;
  1982. var
  1983. Len: Integer;
  1984. Buffer: array[Byte] of Char;
  1985. begin
  1986. while True do
  1987. begin
  1988. while (P[0] <> #0) and (P[0] <= ' ') do Inc(P);
  1989. if (P[0] = '"') and (P[1] = '"') then Inc(P, 2) else Break;
  1990. end;
  1991. Len := 0;
  1992. while P[0] > ' ' do
  1993. if P[0] = '"' then
  1994. begin
  1995. Inc(P);
  1996. while (P[0] <> #0) and (P[0] <> '"') do
  1997. begin
  1998. Buffer[Len] := P[0];
  1999. Inc(Len);
  2000. Inc(P);
  2001. end;
  2002. if P[0] <> #0 then Inc(P);
  2003. end
  2004. else
  2005. begin
  2006. Buffer[Len] := P[0];
  2007. Inc(Len);
  2008. Inc(P);
  2009. end;
  2010. SetString(Param, Buffer, Len);
  2011. Result := P;
  2012. end;
  2013. function ParamCountFromCommandLine(CmdLine: PChar): Integer;
  2014. var
  2015. S: string;
  2016. P: PChar;
  2017. begin
  2018. P := CmdLine;
  2019. Result := 0;
  2020. while True do
  2021. begin
  2022. P := GetParamStr(P, S);
  2023. if S = '' then Break;
  2024. Inc(Result);
  2025. end;
  2026. end;
  2027. function ParamStrFromCommandLine(CmdLine: PChar; Index: Integer): string;
  2028. var
  2029. P: PChar;
  2030. begin
  2031. P := CmdLine;
  2032. while True do
  2033. begin
  2034. P := GetParamStr(P, Result);
  2035. if (Index = 0) or (Result = '') then Break;
  2036. Dec(Index);
  2037. end;
  2038. end;
  2039. procedure SplitCommandLine(const CmdLine: string; var ExeName,
  2040. Params: string);
  2041. var
  2042. Buffer: PChar;
  2043. Cnt, I: Integer;
  2044. S: string;
  2045. begin
  2046. ExeName := '';
  2047. Params := '';
  2048. Buffer := StrPAlloc(CmdLine);
  2049. try
  2050. Cnt := ParamCountFromCommandLine(Buffer);
  2051. if Cnt > 0 then
  2052. begin
  2053. ExeName := ParamStrFromCommandLine(Buffer, 0);
  2054. for I := 1 to Cnt - 1 do
  2055. begin
  2056. S := ParamStrFromCommandLine(Buffer, I);
  2057. if Pos(' ', S) > 0 then S := '"' + S + '"';
  2058. Params := Params + S;
  2059. if I < Cnt - 1 then Params := Params + ' ';
  2060. end;
  2061. end;
  2062. finally
  2063. StrDispose(Buffer);
  2064. end;
  2065. end;
  2066. function AnsiUpperFirstChar(const S: string): string;
  2067. {$IFNDEF RX_D12}
  2068. var
  2069. Temp: string[1];
  2070. {$ENDIF}
  2071. begin
  2072. Result := AnsiLowerCase(S);
  2073. if S <> '' then
  2074. {$IFDEF RX_D12}
  2075. Result[1]:= ToUpper(Result[1]);
  2076. {$ELSE}
  2077. begin
  2078. Temp := Result[1];
  2079. Temp := AnsiUpperCase(Temp);
  2080. Result[1] := Char(Temp[1]);
  2081. end;
  2082. {$ENDIF}
  2083. end;
  2084. function StrPAlloc(const S: string): PChar;
  2085. begin
  2086. Result := StrPCopy(StrAlloc(Length(S) + 1), S);
  2087. end;
  2088. function StringToPChar(var S: string): PChar;
  2089. begin
  2090. {$IFNDEF VER80}
  2091. Result := PChar(S);
  2092. {$ELSE}
  2093. if Length(S) = High(S) then Dec(S[0]);
  2094. S[Length(S) + 1] := #0;
  2095. Result := @(S[1]);
  2096. {$ENDIF}
  2097. end;
  2098. function DropT(const S: string): string;
  2099. begin
  2100. if (UpCase(S[1]) = 'T') and (Length(S) > 1) then
  2101. Result := Copy(S, 2, System.MaxInt)
  2102. else Result := S;
  2103. end;
  2104. { Cursor routines }
  2105. {$IFNDEF VER80}
  2106. {$IFNDEF RX_D3}
  2107. const
  2108. RT_ANICURSOR = MakeIntResource(21);
  2109. {$ENDIF}
  2110. function LoadAniCursor(Instance: THandle; ResID: PChar): HCursor;
  2111. { Unfortunately I don't know how we can load animated cursor from
  2112. executable resource directly. So I write this routine using temporary
  2113. file and LoadCursorFromFile function. }
  2114. var
  2115. S: TFileStream;
  2116. Path, FileName: array[0..MAX_PATH] of Char;
  2117. Rsrc: HRSRC;
  2118. Res: THandle;
  2119. Data: Pointer;
  2120. begin
  2121. Result := 0;
  2122. Rsrc := FindResource(Instance, ResID, RT_ANICURSOR);
  2123. if Rsrc <> 0 then
  2124. begin
  2125. Win32Check(GetTempPath(MAX_PATH, Path) <> 0);
  2126. Win32Check(GetTempFileName(Path, 'ANI', 0, FileName) <> 0);
  2127. try
  2128. Res := LoadResource(Instance, Rsrc);
  2129. try
  2130. Data := LockResource(Res);
  2131. if Data <> nil then
  2132. try
  2133. S := TFileStream.Create(StrPas(FileName), fmCreate);
  2134. try
  2135. S.WriteBuffer(Data^, SizeOfResource(Instance, Rsrc));
  2136. finally
  2137. S.Free;
  2138. end;
  2139. Result := LoadCursorFromFile(FileName);
  2140. finally
  2141. UnlockResource(Res);
  2142. end;
  2143. finally
  2144. FreeResource(Res);
  2145. end;
  2146. finally
  2147. Windows.DeleteFile(FileName);
  2148. end;
  2149. end;
  2150. end;
  2151. {$ENDIF}
  2152. function DefineCursor(Instance: THandle; ResID: PChar): TCursor;
  2153. var
  2154. Handle: HCursor;
  2155. begin
  2156. Handle := LoadCursor(Instance, ResID);
  2157. {$IFNDEF VER80}
  2158. if Handle = 0 then
  2159. Handle := LoadAniCursor(Instance, ResID);
  2160. {$ENDIF}
  2161. if Handle = 0 then ResourceNotFound(ResID);
  2162. for Result := 100 to High(TCursor) do { Look for an unassigned cursor index }
  2163. if (Screen.Cursors[Result] = Screen.Cursors[crDefault]) then
  2164. begin
  2165. Screen.Cursors[Result] := Handle;
  2166. Exit;
  2167. end;
  2168. DestroyCursor(Handle);
  2169. raise EOutOfResources.Create(ResStr(SOutOfResources));
  2170. end;
  2171. const
  2172. WaitCount: Integer = 0;
  2173. SaveCursor: TCursor = crDefault;
  2174. procedure StartWait;
  2175. begin
  2176. if WaitCount = 0 then
  2177. begin
  2178. SaveCursor := Screen.Cursor;
  2179. Screen.Cursor := WaitCursor;
  2180. end;
  2181. Inc(WaitCount);
  2182. end;
  2183. procedure StopWait;
  2184. begin
  2185. if WaitCount > 0 then
  2186. begin
  2187. Dec(WaitCount);
  2188. if WaitCount = 0 then Screen.Cursor := SaveCursor;
  2189. end;
  2190. end;
  2191. { Grid drawing }
  2192. const
  2193. DrawBitmap: TBitmap = nil;
  2194. procedure UsesBitmap;
  2195. begin
  2196. if DrawBitmap = nil then DrawBitmap := TBitmap.Create;
  2197. end;
  2198. procedure ReleaseBitmap; far;
  2199. begin
  2200. if DrawBitmap <> nil then DrawBitmap.Free;
  2201. DrawBitmap := nil;
  2202. end;
  2203. procedure WriteText(ACanvas: TCanvas; ARect: TRect; DX, DY: Integer;
  2204. const Text: string; Alignment: TAlignment; WordWrap: Boolean
  2205. {$IFDEF RX_D4}; ARightToLeft: Boolean = False {$ENDIF});
  2206. const
  2207. AlignFlags: array [TAlignment] of Integer =
  2208. (DT_LEFT or DT_EXPANDTABS or DT_NOPREFIX,
  2209. DT_RIGHT or DT_EXPANDTABS or DT_NOPREFIX,
  2210. DT_CENTER or DT_EXPANDTABS or DT_NOPREFIX);
  2211. WrapFlags: array[Boolean] of Integer = (0, DT_WORDBREAK);
  2212. {$IFDEF RX_D4}
  2213. RTL: array [Boolean] of Integer = (0, DT_RTLREADING);
  2214. {$ENDIF}
  2215. var
  2216. {$IFDEF VER80}
  2217. S: array[0..255] of Char;
  2218. {$ENDIF}
  2219. B, R: TRect;
  2220. I, Left: Integer;
  2221. begin
  2222. UsesBitmap;
  2223. I := ColorToRGB(ACanvas.Brush.Color);
  2224. if not WordWrap and (Integer(GetNearestColor(ACanvas.Handle, I)) = I) and (Pos(#13, Text) = 0) then
  2225. begin { Use ExtTextOut for solid colors }
  2226. {$IFDEF RX_D4}
  2227. { In BiDi, because we changed the window origin, the text that does not
  2228. change alignment, actually gets its alignment changed. }
  2229. if (ACanvas.CanvasOrientation = coRightToLeft) and (not ARightToLeft) then
  2230. ChangeBiDiModeAlignment(Alignment);
  2231. {$ENDIF}
  2232. case Alignment of
  2233. taLeftJustify: Left := ARect.Left + DX;
  2234. taRightJustify: Left := ARect.Right - ACanvas.TextWidth(Text) - 3;
  2235. else { taCenter }
  2236. Left := ARect.Left + (ARect.Right - ARect.Left) shr 1 - (ACanvas.TextWidth(Text) shr 1);
  2237. end;
  2238. {$IFDEF RX_D4}
  2239. ACanvas.TextRect(ARect, Left, ARect.Top + DY, Text);
  2240. {$ELSE}
  2241. {$IFNDEF VER80}
  2242. ExtTextOut(ACanvas.Handle, Left, ARect.Top + DY, ETO_OPAQUE or
  2243. ETO_CLIPPED, @ARect, PChar(Text), Length(Text), nil);
  2244. {$ELSE}
  2245. ExtTextOut(ACanvas.Handle, Left, ARect.Top + DY, ETO_OPAQUE or
  2246. ETO_CLIPPED, @ARect, StrPCopy(S, Text), Length(Text), nil);
  2247. {$ENDIF}
  2248. {$ENDIF}
  2249. end
  2250. else begin { Use FillRect and DrawText for dithered colors }
  2251. {$IFDEF RX_D3}
  2252. DrawBitmap.Canvas.Lock;
  2253. try
  2254. {$ENDIF}
  2255. with DrawBitmap, ARect do { Use offscreen bitmap to eliminate flicker and }
  2256. begin { brush origin tics in painting / scrolling. }
  2257. Width := Max(Width, Right - Left);
  2258. Height := Max(Height, Bottom - Top);
  2259. R := Rect(DX, DY, Right - Left - {$IFNDEF VER80} 1 {$ELSE} 2 {$ENDIF},
  2260. Bottom - Top - 1);
  2261. B := Rect(0, 0, Right - Left, Bottom - Top);
  2262. end;
  2263. with DrawBitmap.Canvas do
  2264. begin
  2265. Font := ACanvas.Font;
  2266. Font.Color := ACanvas.Font.Color;
  2267. Brush := ACanvas.Brush;
  2268. Brush.Style := bsSolid;
  2269. FillRect(B);
  2270. SetBkMode(Handle, TRANSPARENT);
  2271. {$IFDEF RX_D4}
  2272. if (ACanvas.CanvasOrientation = coRightToLeft) then
  2273. ChangeBiDiModeAlignment(Alignment);
  2274. DrawText(Handle, PChar(Text), Length(Text), R, AlignFlags[Alignment]
  2275. or RTL[ARightToLeft] or WrapFlags[WordWrap]);
  2276. {$ELSE}
  2277. {$IFNDEF VER80}
  2278. DrawText(Handle, PChar(Text), Length(Text), R,
  2279. AlignFlags[Alignment] or WrapFlags[WordWrap]);
  2280. {$ELSE}
  2281. DrawText(Handle, StrPCopy(S, Text), Length(Text), R,
  2282. AlignFlags[Alignment] or WrapFlags[WordWrap]);
  2283. {$ENDIF}
  2284. {$ENDIF}
  2285. end;
  2286. ACanvas.CopyRect(ARect, DrawBitmap.Canvas, B);
  2287. {$IFDEF RX_D3}
  2288. finally
  2289. DrawBitmap.Canvas.Unlock;
  2290. end;
  2291. {$ENDIF}
  2292. end;
  2293. end;
  2294. {$IFDEF RX_D4}
  2295. procedure DrawCellTextEx(Control: TCustomControl; ACol, ARow: Longint;
  2296. const S: string; const ARect: TRect; Align: TAlignment;
  2297. VertAlign: TVertAlignment; WordWrap: Boolean; ARightToLeft: Boolean);
  2298. const
  2299. MinOffs = 2;
  2300. var
  2301. H: Integer;
  2302. begin
  2303. case VertAlign of
  2304. vaTopJustify: H := MinOffs;
  2305. vaCenter:
  2306. with THack(Control) do
  2307. H := Max(1, (ARect.Bottom - ARect.Top - Canvas.TextHeight('W')) div 2);
  2308. else {vaBottomJustify}
  2309. with THack(Control) do
  2310. H := Max(MinOffs, ARect.Bottom - ARect.Top - Canvas.TextHeight('W'));
  2311. end;
  2312. WriteText(THack(Control).Canvas, ARect, MinOffs, H, S, Align, WordWrap,
  2313. ARightToLeft);
  2314. end;
  2315. procedure DrawCellText(Control: TCustomControl; ACol, ARow: Longint;
  2316. const S: string; const ARect: TRect; Align: TAlignment;
  2317. VertAlign: TVertAlignment; ARightToLeft: Boolean);
  2318. begin
  2319. DrawCellTextEx(Control, ACol, ARow, S, ARect, Align, VertAlign,
  2320. Align = taCenter, ARightToLeft);
  2321. end;
  2322. {$ENDIF}
  2323. procedure DrawCellTextEx(Control: TCustomControl; ACol, ARow: Longint;
  2324. const S: string; const ARect: TRect; Align: TAlignment;
  2325. VertAlign: TVertAlignment; WordWrap: Boolean);
  2326. const
  2327. MinOffs = 2;
  2328. var
  2329. H: Integer;
  2330. begin
  2331. case VertAlign of
  2332. vaTopJustify: H := MinOffs;
  2333. vaCenter:
  2334. with THack(Control) do
  2335. H := Max(1, (ARect.Bottom - ARect.Top - Canvas.TextHeight('W')) div 2);
  2336. else {vaBottomJustify}
  2337. with THack(Control) do
  2338. H := Max(MinOffs, ARect.Bottom - ARect.Top - Canvas.TextHeight('W'));
  2339. end;
  2340. WriteText(THack(Control).Canvas, ARect, MinOffs, H, S, Align, WordWrap);
  2341. end;
  2342. procedure DrawCellText(Control: TCustomControl; ACol, ARow: Longint;
  2343. const S: string; const ARect: TRect; Align: TAlignment;
  2344. VertAlign: TVertAlignment);
  2345. begin
  2346. DrawCellTextEx(Control, ACol, ARow, S, ARect, Align, VertAlign,
  2347. Align = taCenter);
  2348. end;
  2349. procedure DrawCellBitmap(Control: TCustomControl; ACol, ARow: Longint;
  2350. Bmp: TGraphic; Rect: TRect);
  2351. begin
  2352. Rect.Top := (Rect.Bottom + Rect.Top - Bmp.Height) div 2;
  2353. Rect.Left := (Rect.Right + Rect.Left - Bmp.Width) div 2;
  2354. THack(Control).Canvas.Draw(Rect.Left, Rect.Top, Bmp);
  2355. end;
  2356. { TScreenCanvas }
  2357. destructor TScreenCanvas.Destroy;
  2358. begin
  2359. FreeHandle;
  2360. inherited Destroy;
  2361. end;
  2362. procedure TScreenCanvas.CreateHandle;
  2363. begin
  2364. if FDeviceContext = 0 then FDeviceContext := GetDC(0);
  2365. Handle := FDeviceContext;
  2366. end;
  2367. procedure TScreenCanvas.FreeHandle;
  2368. begin
  2369. if FDeviceContext <> 0 then
  2370. begin
  2371. Handle := 0;
  2372. ReleaseDC(0, FDeviceContext);
  2373. FDeviceContext := 0;
  2374. end;
  2375. end;
  2376. procedure TScreenCanvas.SetOrigin(X, Y: Integer);
  2377. var
  2378. FOrigin: TPoint;
  2379. begin
  2380. SetWindowOrgEx(Handle, -X, -Y, @FOrigin);
  2381. end;
  2382. {$IFDEF VER80}
  2383. { TBits }
  2384. const
  2385. BitsPerInt = SizeOf(Integer) * 8;
  2386. type
  2387. TBitEnum = 0..BitsPerInt - 1;
  2388. TBitSet = set of TBitEnum;
  2389. PBitArray = ^TBitArray;
  2390. TBitArray = array[0..4096] of TBitSet;
  2391. destructor TBits.Destroy;
  2392. begin
  2393. SetSize(0);
  2394. inherited Destroy;
  2395. end;
  2396. procedure TBits.SetSize(Value: Integer);
  2397. var
  2398. NewMem: Pointer;
  2399. NewMemSize: Integer;
  2400. OldMemSize: Integer;
  2401. begin
  2402. if Value <> Size then
  2403. begin
  2404. NewMemSize := ((Value + BitsPerInt - 1) div BitsPerInt) * SizeOf(Integer);
  2405. OldMemSize := ((Size + BitsPerInt - 1) div BitsPerInt) * SizeOf(Integer);
  2406. if NewMemSize <> OldMemSize then
  2407. begin
  2408. NewMem := nil;
  2409. if NewMemSize <> 0 then
  2410. begin
  2411. GetMem(NewMem, NewMemSize);
  2412. FillChar(NewMem^, NewMemSize, 0);
  2413. end
  2414. else NewMem := nil;
  2415. if OldMemSize <> 0 then
  2416. begin
  2417. if NewMem <> nil then
  2418. Move(FBits^, NewMem^, Min(OldMemSize, NewMemSize));
  2419. FreeMem(FBits, OldMemSize);
  2420. end;
  2421. FBits := NewMem;
  2422. end;
  2423. FSize := Value;
  2424. end;
  2425. end;
  2426. procedure TBits.SetBit(Index: Integer; Value: Boolean);
  2427. begin
  2428. if Value then
  2429. Include(PBitArray(FBits)^[Index div BitsPerInt], Index mod BitsPerInt)
  2430. else
  2431. Exclude(PBitArray(FBits)^[Index div BitsPerInt], Index mod BitsPerInt);
  2432. end;
  2433. function TBits.GetBit(Index: Integer): Boolean;
  2434. begin
  2435. Result := Index mod BitsPerInt in PBitArray(FBits)^[Index div BitsPerInt];
  2436. end;
  2437. function TBits.OpenBit: Integer;
  2438. var
  2439. I: Integer;
  2440. B: TBitSet;
  2441. J: TBitEnum;
  2442. E: Integer;
  2443. begin
  2444. E := (Size + BitsPerInt - 1) div BitsPerInt - 1;
  2445. for I := 0 to E do
  2446. if PBitArray(FBits)^[I] <> [0..BitsPerInt - 1] then
  2447. begin
  2448. B := PBitArray(FBits)^[I];
  2449. for J := Low(J) to High(J) do
  2450. begin
  2451. if not (J in B) then
  2452. begin
  2453. Result := I * BitsPerInt + J;
  2454. if Result >= Size then Result := Size;
  2455. Exit;
  2456. end;
  2457. end;
  2458. end;
  2459. Result := Size;
  2460. end;
  2461. (*
  2462. To create a metafile image from scratch, you must draw the image in
  2463. a metafile canvas. When the canvas is destroyed, it transfers the
  2464. image into the metafile object provided to the canvas constructor.
  2465. After the image is drawn on the canvas and the canvas is destroyed,
  2466. the image is 'playable' in the metafile object. Like this:
  2467. MyMetafile := TMetafile.Create;
  2468. with TMetafileCanvas.Create(MyMetafile, 0) do
  2469. try
  2470. Brush.Color := clRed;
  2471. Ellipse(0,0,100,100);
  2472. ...
  2473. finally
  2474. Free;
  2475. end;
  2476. Form1.Canvas.Draw(0,0,MyMetafile); { 1 red circle }
  2477. To add to an existing metafile image, create a metafile canvas
  2478. and play the source metafile into the metafile canvas. Like this:
  2479. { continued from previous example, so MyMetafile contains an image }
  2480. with TMetafileCanvas.Create(MyMetafile, 0) do
  2481. try
  2482. Draw(0,0,MyMetafile);
  2483. Brush.Color := clBlue;
  2484. Ellipse(100,100,200,200);
  2485. ...
  2486. finally
  2487. Free;
  2488. end;
  2489. Form1.Canvas.Draw(0,0,MyMetafile); { 1 red circle and 1 blue circle }
  2490. *)
  2491. constructor TMetafileCanvas.Create(AMetafile: TMetafile; ReferenceDevice: HDC);
  2492. var
  2493. Temp: HDC;
  2494. begin
  2495. inherited Create;
  2496. FMetafile := AMetafile;
  2497. Temp := CreateMetafile(nil);
  2498. if Temp = 0 then
  2499. raise EOutOfResources.Create(ResStr(SOutOfResources));
  2500. Handle := Temp;
  2501. FMetafile.Inch := Screen.PixelsPerInch;
  2502. end;
  2503. destructor TMetafileCanvas.Destroy;
  2504. var
  2505. Temp: HDC;
  2506. KeepInch, KeepWidth, KeepHeight: Integer;
  2507. begin
  2508. Temp := Handle;
  2509. Handle := 0;
  2510. with FMetafile do
  2511. begin
  2512. KeepWidth := Width;
  2513. KeepHeight := Height;
  2514. KeepInch := Inch;
  2515. Handle := CloseMetafile(Temp);
  2516. Width := KeepWidth;
  2517. Height := KeepHeight;
  2518. Inch := KeepInch;
  2519. end;
  2520. inherited Destroy;
  2521. end;
  2522. { TResourceStream }
  2523. constructor TResourceStream.Create(Instance: THandle; const ResName: string;
  2524. ResType: PChar);
  2525. var
  2526. ResID: array[0..255] of Char;
  2527. begin
  2528. CreateFromPChar(Instance, StrPCopy(ResID, ResName), ResType);
  2529. end;
  2530. constructor TResourceStream.CreateFromID(Instance: THandle; ResID: Integer;
  2531. ResType: PChar);
  2532. begin
  2533. CreateFromPChar(Instance, MakeIntResource(ResID), ResType);
  2534. end;
  2535. constructor TResourceStream.CreateFromPChar(Instance: THandle; ResName,
  2536. ResType: PChar);
  2537. var
  2538. ResInfo: THandle;
  2539. Handle: Integer;
  2540. begin
  2541. ResInfo := FindResource(Instance, ResName, ResType);
  2542. if ResInfo = 0 then ResourceNotFound(ResName);
  2543. Handle := AccessResource(Instance, ResInfo);
  2544. if Handle < 0 then ResourceNotFound(ResName);
  2545. inherited Create(Handle);
  2546. FStartPos := inherited Seek(0, soFromCurrent);
  2547. FEndPos := FStartPos + SizeOfResource(Instance, ResInfo);
  2548. end;
  2549. destructor TResourceStream.Destroy;
  2550. begin
  2551. if Handle >= 0 then FileClose(Handle);
  2552. inherited Destroy;
  2553. end;
  2554. function TResourceStream.Write(const Buffer; Count: Longint): Longint;
  2555. begin
  2556. raise EStreamError.CreateRes(SWriteError);
  2557. end;
  2558. function TResourceStream.Seek(Offset: Longint; Origin: Word): Longint;
  2559. begin
  2560. case Origin of
  2561. soFromBeginning:
  2562. Result := inherited Seek(FStartPos + Offset, Origin) - FStartPos;
  2563. soFromCurrent:
  2564. Result := inherited Seek(Offset, Origin) - FStartPos;
  2565. soFromEnd:
  2566. Result := inherited Seek(FEndPos + Offset, soFromBeginning) - FStartPos;
  2567. end;
  2568. if Result > FEndPos then raise EStreamError.CreateRes(SReadError);
  2569. end;
  2570. function GetCurrentDir: string;
  2571. begin
  2572. GetDir(0, Result);
  2573. end;
  2574. {$I-}
  2575. function SetCurrentDir(const Dir: string): Boolean;
  2576. begin
  2577. ChDir(Dir);
  2578. Result := IOResult = 0;
  2579. end;
  2580. {$ENDIF}
  2581. {$IFNDEF VER80}
  2582. procedure RaiseWin32Error(ErrorCode: DWORD);
  2583. {$IFDEF RX_D3}
  2584. var
  2585. {$IFDEF RX_D6} // Polaris
  2586. Error: EOSError;
  2587. {$ELSE}
  2588. Error: EWin32Error;
  2589. {$ENDIF}
  2590. {$ENDIF}
  2591. begin
  2592. if ErrorCode <> ERROR_SUCCESS then
  2593. begin
  2594. {$IFDEF RX_D3}
  2595. {$IFDEF RX_D6} // Polaris
  2596. Error := EOSError.CreateFmt(SOSError, [ErrorCode, SysErrorMessage(ErrorCode)]);
  2597. {$ELSE}
  2598. Error := EWin32Error.CreateFmt(SWin32Error, [ErrorCode, SysErrorMessage(ErrorCode)]);
  2599. {$ENDIF}
  2600. Error.ErrorCode := ErrorCode;
  2601. raise Error;
  2602. {$ELSE}
  2603. raise Exception.CreateFmt('%s (%d)', [SysErrorMessage(ErrorCode), ErrorCode]);
  2604. {$ENDIF}
  2605. end;
  2606. end;
  2607. { Win32Check is used to check the return value of a Win32 API function
  2608. which returns a BOOL to indicate success. }
  2609. {$IFNDEF RX_D3}
  2610. function Win32Check(RetVal: Bool): Bool;
  2611. var
  2612. LastError: DWORD;
  2613. begin
  2614. if not RetVal then
  2615. begin
  2616. LastError := GetLastError;
  2617. raise Exception.CreateFmt('%s (%d)', [SysErrorMessage(LastError),
  2618. LastError]);
  2619. end;
  2620. Result := RetVal;
  2621. end;
  2622. {$ENDIF RX_D3}
  2623. function CheckWin32(OK: Boolean): Boolean;
  2624. begin
  2625. Result := Win32Check(Ok);
  2626. end;
  2627. {$ENDIF}
  2628. {$IFNDEF RX_D3}
  2629. function ResStr(Ident: Cardinal): string;
  2630. begin
  2631. Result := LoadStr(Ident);
  2632. end;
  2633. {$ELSE}
  2634. function ResStr(const Ident: string): string;
  2635. begin
  2636. Result := Ident;
  2637. end;
  2638. {$ENDIF}
  2639. { Check if this is the active Windows task }
  2640. { Copied from implementation of FORMS.PAS }
  2641. type
  2642. PCheckTaskInfo = ^TCheckTaskInfo;
  2643. TCheckTaskInfo = record
  2644. FocusWnd: HWnd;
  2645. Found: Boolean;
  2646. end;
  2647. function CheckTaskWindow(Window: HWnd; Data: Longint): WordBool;
  2648. {$IFNDEF VER80} stdcall {$ELSE} export {$ENDIF};
  2649. begin
  2650. Result := True;
  2651. if PCheckTaskInfo(Data)^.FocusWnd = Window then
  2652. begin
  2653. Result := False;
  2654. PCheckTaskInfo(Data)^.Found := True;
  2655. end;
  2656. end;
  2657. function IsForegroundTask: Boolean;
  2658. var
  2659. Info: TCheckTaskInfo;
  2660. {$IFDEF VER80}
  2661. Proc: TFarProc;
  2662. {$ENDIF}
  2663. begin
  2664. Info.FocusWnd := GetActiveWindow;
  2665. Info.Found := False;
  2666. {$IFNDEF VER80}
  2667. EnumThreadWindows(GetCurrentThreadID, @CheckTaskWindow, Longint(@Info));
  2668. {$ELSE}
  2669. Proc := MakeProcInstance(@CheckTaskWindow, HInstance);
  2670. try
  2671. EnumTaskWindows(GetCurrentTask, Proc, Longint(@Info));
  2672. finally
  2673. FreeProcInstance(Proc);
  2674. end;
  2675. {$ENDIF}
  2676. Result := Info.Found;
  2677. end;
  2678. function GetWindowsVersion: string;
  2679. {$IFNDEF VER80}
  2680. const
  2681. sWindowsVersion = 'Windows %s %d.%.2d.%.3d %s';
  2682. var
  2683. Ver: TOsVersionInfo;
  2684. Platform: string[4];
  2685. begin
  2686. Ver.dwOSVersionInfoSize := SizeOf(Ver);
  2687. GetVersionEx(Ver);
  2688. with Ver do begin
  2689. case dwPlatformId of
  2690. VER_PLATFORM_WIN32s: Platform := '32s';
  2691. VER_PLATFORM_WIN32_WINDOWS:
  2692. begin
  2693. dwBuildNumber := dwBuildNumber and $0000FFFF;
  2694. if (dwMajorVersion > 4) or ((dwMajorVersion = 4) and
  2695. (dwMinorVersion >= 10)) then Platform := '98'
  2696. else Platform := '95';
  2697. end;
  2698. VER_PLATFORM_WIN32_NT: Platform := 'NT';
  2699. end;
  2700. Result := Trim(Format(sWindowsVersion, [Platform, dwMajorVersion,
  2701. dwMinorVersion, dwBuildNumber, szCSDVersion]));
  2702. end;
  2703. end;
  2704. {$ELSE}
  2705. const
  2706. sWindowsVersion = 'Windows%s %d.%d';
  2707. sNT: array[Boolean] of string[3] = ('', ' NT');
  2708. var
  2709. Ver: Longint;
  2710. begin
  2711. Ver := GetVersion;
  2712. Result := Format(sWindowsVersion, [sNT[not Boolean(HiByte(LoWord(Ver)))],
  2713. LoByte(LoWord(Ver)), HiByte(LoWord(Ver))]);
  2714. end;
  2715. {$ENDIF}
  2716. function TextSizeDC(DC: HDC; const Text: string): TSize;
  2717. begin
  2718. GetTextExtentPoint32(DC,PChar(Text),length(Text),Result);
  2719. end;
  2720. function TextSize(Wnd: HWnd; const Text: string): TSize;
  2721. var
  2722. fnt,fnt_org: HFont;
  2723. DC: HDC;
  2724. begin
  2725. fnt:=SendMessage(Wnd,WM_GETFONT,0,0);
  2726. DC:=GetDC(Wnd);
  2727. try
  2728. fnt_org:=SelectObject(DC,fnt);
  2729. Result:=TextSizeDC(DC,Text);
  2730. SelectObject(DC,fnt_org);
  2731. finally
  2732. ReleaseDC(Wnd,DC);
  2733. end;
  2734. end;
  2735. function TextToLinesDC(DC: HDC; const Text: string; MaxLen: Integer): string;
  2736. var
  2737. I: Integer;
  2738. S, w, CurStr: string;
  2739. begin
  2740. Result := '';
  2741. S := '';
  2742. CurStr := '';
  2743. for I := 1 to WordCount(Text, [' ']) do
  2744. begin
  2745. w := ExtractWord(I, Text, [' ']);
  2746. if Length(CurStr) > 0 then
  2747. S := CurStr + ' ' + w
  2748. else
  2749. S := CurStr + w;
  2750. if Length(CurStr) > 0 then
  2751. if TextSizeDC(DC, S).CX > MaxLen then
  2752. begin
  2753. if Length(Result) > 0 then
  2754. Result := Result + #13#10;
  2755. Result := Result + CurStr;
  2756. CurStr := w;
  2757. continue;
  2758. end;
  2759. CurStr := S;
  2760. end;
  2761. if Length(CurStr) > 0 then
  2762. begin
  2763. if Length(Result) > 0 then
  2764. Result := Result + #13#10;
  2765. Result := Result + CurStr;
  2766. end;
  2767. end;
  2768. function TextToLines(Wnd: HWnd; const Text: string; MaxLen: integer): string;
  2769. var
  2770. fnt,fnt_org: HFont;
  2771. DC: HDC;
  2772. begin
  2773. fnt:=SendMessage(Wnd,WM_GETFONT,0,0);
  2774. DC:=GetDC(Wnd);
  2775. try
  2776. fnt_org:=SelectObject(DC,fnt);
  2777. Result:=TextToLinesDC(DC,Text,MaxLen);
  2778. SelectObject(DC,fnt_org);
  2779. finally
  2780. ReleaseDC(Wnd,DC);
  2781. end;
  2782. end;
  2783. initialization
  2784. {$IFNDEF VER80}
  2785. finalization
  2786. ReleaseBitmap;
  2787. {$ELSE}
  2788. AddExitProc(ReleaseBitmap);
  2789. {$ENDIF}
  2790. end.