{*********************************************************}
{*                   OVCMISC.PAS 4.06                    *}
{*********************************************************}

{* ***** BEGIN LICENSE BLOCK *****                                            *}
{* Version: MPL 1.1                                                           *}
{*                                                                            *}
{* The contents of this file are subject to the Mozilla Public License        *}
{* Version 1.1 (the "License"); you may not use this file except in           *}
{* compliance with the License. You may obtain a copy of the License at       *}
{* http://www.mozilla.org/MPL/                                                *}
{*                                                                            *}
{* Software distributed under the License is distributed on an "AS IS" basis, *}
{* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License   *}
{* for the specific language governing rights and limitations under the       *}
{* License.                                                                   *}
{*                                                                            *}
{* The Original Code is TurboPower Orpheus                                    *}
{*                                                                            *}
{* The Initial Developer of the Original Code is TurboPower Software          *}
{*                                                                            *}
{* Portions created by TurboPower Software Inc. are Copyright (C) 1995-2002   *}
{* TurboPower Software Inc. All Rights Reserved.                              *}
{*                                                                            *}
{* Contributor(s):                                                            *}
{*                                                                            *}
{* ***** END LICENSE BLOCK *****                                              *}

(*Changes)

  10/20/01- Hdc changed to TOvcHdc for BCB Compatibility
  10/20/01- HWnd changed to TOvcHWnd for BCB Compatibility
*)

{$I OVC.INC}

{$B-} {Complete Boolean Evaluation}
{$I+} {Input/Output-Checking}
{$P+} {Open Parameters}
{$T-} {Typed @ Operator}
{.W-} {Windows Stack Frame}
{$X+} {Extended Syntax}

unit ovcmisc;
  {-Miscellaneous functions and procedures}

interface

uses
  {$IFNDEF LCL} Windows, Messages, {$ELSE} LclIntf, LMessages, LclType, MyMisc, {$ENDIF} 
  Buttons, Classes, Controls, ExtCtrls, Forms, Graphics,
  SysUtils, {$IFNDEF LCL} Consts, {$ELSE} LclStrConsts, {$ENDIF} OvcData;

{ Hdc needs to be an Integer for BCB compatibility }
{$IFDEF CBuilder}
type
  TOvcHdc  = Integer;
  TOvcHWND = Cardinal;
{$ELSE}
type
  TOvcHdc  = HDC;
  TOvcHWND = HWND;
{$ENDIF}

{$IFNDEF LCL}
function LoadBaseBitmap(lpBitmapName : PAnsiChar) : HBITMAP;
  {-load and return the handle to bitmap resource}
function LoadBaseCursor(lpCursorName : PAnsiChar) : HCURSOR;
  {-load and return the handle to cursor resource}
{$ENDIF}
function CompStruct(const S1, S2; Size : Cardinal) : Integer;
  {-compare two fixed size structures}
function DefaultEpoch : Integer;
  {-return the current century}
function DrawButtonFrame(Canvas : TCanvas; const Client : TRect;
  IsDown, IsFlat : Boolean; Style : TButtonStyle) : TRect;
  {-produce a button similar to DrawFrameControl}
procedure FixRealPrim(P : PAnsiChar; DC : AnsiChar);
  {-get a PChar string representing a real ready for Val()}
function GetDisplayString(Canvas : TCanvas; const S : string;
  MinChars, MaxWidth : Integer) : string;
  {-given a string, a minimum number of chars to display, and a max width,
    find the string that can be displayed in that width - add ellipsis to
    the end if necessary and possible}
function GetLeftButton: Byte;
  {-return the mapped left button}

{ - HWnd changed to TOvcHWnd for BCB Compatibility }
function GetNextDlgItem(Ctrl : TOvcHWnd{hWnd}) : hWnd;

  {-get handle of next control in the same form}
procedure GetRGB(Clr : TColor; var IR, IG, IB : Byte);
  {-return component parts of the rgb value}
function GetShiftFlags : Byte;
  {-get current shift flags, the high order bit is set if the key is down}
function CreateRotatedFont(F : TFont; Angle : Integer) : hFont;
  {-create a rotated font based on the font object F}
function GetTopTextMargin(Font : TFont; BorderStyle : TBorderStyle;
         Height : Integer; Ctl3D : Boolean) : Integer;
  {-return the pixel top margin size}
function ExtractWord(N : Integer; const S : string; WordDelims : TCharSet) : string;
  {-return the Nth word from S}
function IsForegroundTask : Boolean;
  {-returns true if this task is currently in the foreground}
function TrimLeft(const S : string) : string;
  {-return a string with leading white space removed}
function TrimRight(const S : string) : string;
  {-return a string with trailing white space removed}
function QuotedStr(const S : string) : string;
  {-return a quoted string string with internal quotes escaped}
function WordCount(const S : string; const WordDelims : TCharSet) : Integer;
  {-return the word count given a set of word delimiters}
function WordPosition(const N : Integer; const S : string; const WordDelims : TCharSet) : Integer;
  {-return start position of N'th word in S}
function PtrDiff(const P1, P2) : Word;
  {-return the difference between P1 and P2}
procedure PtrInc(var P; Delta : Word);
  {-increase P by Delta}
procedure PtrDec(var P; Delta : Word);
  {-decrease P by Delta}
procedure FixTextBuffer(InBuf, OutBuf : PChar; OutSize : Integer);
  {-replace orphan linefeeds with cr/lf pairs}

{ - Hdc changed to TOvcHdc for BCB Compatibility }
procedure TransStretchBlt(DstDC: TOvcHdc{HDC}; DstX, DstY, DstW, DstH: Integer;
                           SrcDC: TOvcHdc{HDC}; SrcX, SrcY, SrcW, SrcH: Integer;
                           MaskDC: TOvcHdc{HDC};
                           MaskX, MaskY : Integer);
function MaxL(A, B : LongInt) : LongInt;
function MinL(A, B : LongInt) : LongInt;
function MinI(X, Y : Integer) : Integer;
  {-return the minimum of two integers}
function MaxI(X, Y : Integer) : Integer;
  {-return the maximum of two integers}

{function GenerateComponentName(PF : TCustomForm; const Root : string) : string;}
function GenerateComponentName(PF : TWinControl; const Root : string) : string;
  {-return a component name unique for this form}
function PartialCompare(const S1, S2 : string) : Boolean;
  {-compare minimum length of S1 and S2 strings}

function PathEllipsis(const S : string; MaxWidth : Integer) : string;
function CreateDisabledBitmap(FOriginal : TBitmap; OutlineColor : TColor) : TBitmap;
procedure CopyParentImage(Control : TControl; Dest : TCanvas);
procedure DrawTransparentBitmap(Dest : TCanvas; X, Y, W, H : Integer;
  Rect : TRect; Bitmap : TBitmap; TransparentColor : TColor);
function WidthOf(const R : TRect) : Integer;
  {returnd R.Right - R.Left}
function HeightOf(const R : TRect) : Integer;
  {returnd R.Bottom - R.Top}
procedure DebugOutput(const S : string);
  {use OutputDebugString()}

implementation

uses
  OvcBase, OvcStr;

{$IFNDEF LCL}
function LoadBaseBitmap(lpBitmapName : PAnsiChar) : HBITMAP;
begin
  Result := LoadBitmap(FindClassHInstance(TOvcCustomControlEx), lpBitmapName);
end;

function LoadBaseCursor(lpCursorName : PAnsiChar) : HCURSOR;
begin
  Result := LoadCursor(FindClassHInstance(TOvcCustomControlEx), lpCursorName);
end;
{$ENDIF}

{$IFDEF NoAsm}
function CompStruct(const S1, S2; Size : Cardinal) : Integer;
// Since CompStruct currently only used elsewhere to determine if
//  two structures are different, just use CompareMem and don't
//  worry about whether "greater than" or "less than". 
begin
  if CompareMem(@S1, @S2, Size) then  {Structures identical?}
    Result := 0
  else  {Structures not identical, so just return as though S1 > S2}
    Result := 1;
end;

{$ELSE}
function CompStruct(const S1, S2; Size : Cardinal) : Integer; register;
  {-compare two fixed size structures}
asm
  push    esi
  push    edi

  mov     esi, eax     {pointer to S1}
  mov     edi, edx     {pointer to S2}

  xor     eax, eax     {eax holds temporary result (Equal)}

  or      ecx, ecx     {size is already in ecx}
  jz      @@CSDone     {make sure size isn't zero}

  cld                  {go forward}
  repe    cmpsb        {compare until no match or ecx = 0}

  je      @@CSDone     {if equal, result is already in eax}
  inc     eax          {prepare for greater}
  ja      @@CSDone     {S1 greater? return +1}
  mov     eax, -1      {else S1 less, return -1}

@@CSDone:

  pop     edi
  pop     esi
end;
{$ENDIF}

procedure FixRealPrim(P : PAnsiChar; DC : AnsiChar);
  {-Get a string representing a real ready for Val()}
var
  DotPos : Cardinal;
  EPos   : Cardinal;
  Len    : Word;
  Found  : Boolean;
  EFound : Boolean;
begin
  TrimAllSpacesPChar(P);

  Len := StrLen(P);
  if Len > 0 then begin
    if P[Len-1] = DC then begin
      Dec(Len);
      P[Len] := #0;
      TrimAllSpacesPChar(P);
    end;

    {Val doesn't accept alternate decimal point chars}
    Found := StrChPos(P, DC, DotPos);
    {replace with '.'}
    if Found and (DotPos > 0) then
      P[DotPos] := '.'
    else
      Found := StrChPos(P, pmDecimalPt, DotPos);

    if Found then begin
      {check for 'nnnn.'}
      if LongInt(DotPos) = Len-1 then begin
        P[Len] := '0';
        Inc(Len);
        P[Len] := #0;
      end;

      {check for '.nnnn'}
      if DotPos = 0 then begin
        StrChInsertPrim(P, '0', 0);
        Inc(Len);
        Inc(DotPos);
      end;

      {check for '-.nnnn'}
      if (Len > 1) and (P^ = '-') and (DotPos = 1) then begin
        StrChInsertPrim(P, '0', 1);
        Inc(DotPos);
      end;

    end;

    {fix up numbers with exponents}
    EFound := StrChPos(P, 'E', EPos);
    if EFound and (EPos > 0) then begin
      if not Found then begin
        StrChInsertPrim(P, '.', EPos);
        DotPos := EPos;
        Inc(EPos);
      end;
      if EPos-DotPos < 12 then
        StrStInsertPrim(P, '00000', EPos);
    end;

    {remove blanks before and after '.' }
    if Found then begin
      while (DotPos > 0) and (P[DotPos-1] = ' ') do begin
        StrStDeletePrim(P, DotPos-1, 1);
        Dec(DotPos);
      end;
      while P[DotPos+1] = ' ' do
        StrStDeletePrim(P, DotPos+1, 1);
    end;

  end else begin
    {empty string = '0'}
    P[0] := '0';
    P[1] := #0;
  end;
end;

function GetLeftButton: Byte;
const
  RLButton : array[Boolean] of Word = (VK_LBUTTON, VK_RBUTTON);
begin
  Result := RLButton[GetSystemMetrics(SM_SWAPBUTTON) <> 0];
end;

{ - HWnd changed to TOvcHWnd for BCB Compatibility }
function GetNextDlgItem(Ctrl : TOvcHWnd{hWnd}) : hWnd;
  {-Get handle of next control in the same form}
begin
  {asking for previous returns next}
  Result := GetNextWindow(Ctrl, GW_HWNDPREV);
  if Result = 0 then begin
    {asking for last returns first}
    Result := GetWindow(Ctrl, GW_HWNDLAST);
    if Result = 0 then
      Result := Ctrl;
  end;
end;

procedure GetRGB(Clr : TColor; var IR, IG, IB : Byte);
begin
  if (Clr < 0) then begin
    Clr := Clr + MaxLongInt + 1;
    Clr := GetSysColor(Clr);
  end;
  IR := GetRValue(Clr);
  IG := GetGValue(Clr);
  IB := GetBValue(Clr);
end;

function GetShiftFlags : Byte;
  {-get current shift flags, the high order bit is set if the key is down}
begin
  Result := (Ord(GetKeyState(VK_CONTROL) < 0) * ss_Ctrl) +
            (Ord(GetKeyState(VK_SHIFT ) < 0) * ss_Shift) +
            (Ord(GetKeyState(VK_ALT   ) < 0) * ss_Alt);
end;

function CreateRotatedFont(F : TFont; Angle : Integer) : hFont;
  {-create a rotated font based on the font object F}
var
  LF : TLogFont;
begin
  FillChar(LF, SizeOf(LF), #0);
  with LF do begin
    lfHeight           := F.Height;
    lfWidth            := 0;
    lfEscapement       := Angle*10;
    lfOrientation      := 0;
    if fsBold in F.Style then
      lfWeight         := FW_BOLD
    else
      lfWeight         := FW_NORMAL;
    lfItalic           := Byte(fsItalic in F.Style);
    lfUnderline        := Byte(fsUnderline in F.Style);
    lfStrikeOut        := Byte(fsStrikeOut in F.Style);
    lfCharSet          := DEFAULT_CHARSET;
    StrPCopy(lfFaceName, F.Name);
    lfQuality          := DEFAULT_QUALITY;
    {everything else as default}
    lfOutPrecision     := OUT_DEFAULT_PRECIS;
    lfClipPrecision    := CLIP_DEFAULT_PRECIS;
    case F.Pitch of
      fpVariable : lfPitchAndFamily := VARIABLE_PITCH;
      fpFixed    : lfPitchAndFamily := FIXED_PITCH;
    else
      lfPitchAndFamily := DEFAULT_PITCH;
    end;
  end;
  Result := CreateFontIndirect(LF);
end;

function GetTopTextMargin(Font : TFont; BorderStyle : TBorderStyle;
         Height : Integer; Ctl3D : Boolean) : Integer;
  {-return the pixel top margin size}
var
  I          : Integer;
  DC         : hDC;
  Metrics    : TTextMetric;
  SaveFont   : hFont;
  SysMetrics : TTextMetric;
begin
  DC := GetDC(0);
  try
    GetTextMetrics(DC, SysMetrics);
    SaveFont := SelectObject(DC, Font.Handle);
    GetTextMetrics(DC, Metrics);
    SelectObject(DC, SaveFont);
  finally
    ReleaseDC(0, DC);
  end;
  I := SysMetrics.tmHeight;
  if I > Metrics.tmHeight then
    I := Metrics.tmHeight;

  if NewStyleControls then begin
    if BorderStyle = bsNone then begin
      Result := 0;
      if I >= Height-2 then
        Result := (Height-I-2) div 2 - Ord(Odd(Height-I));
    end else if Ctl3D then begin
      Result := 1;
      if I >= Height-4 then
        Result := (Height-I-4) div 2 - 1;
    end else begin
      Result := 1;
      if I >= Height-4 then
        Result := (Height-I-4) div 2 - Ord(Odd(Height-I));
    end;
  end else begin
    Result := (Height-Metrics.tmHeight-1) div 2;
    if I > Height-2 then begin
      Dec(Result, 2);
      if BorderStyle = bsNone then
        Inc(Result, 1);
    end;
  end;
end;

function PtrDiff(const P1, P2) : Word;
  {-return the difference between P1 and P2}
begin
  {P1 and P2 are assumed to point within the same buffer}
  Result := PAnsiChar(P1) - PAnsiChar(P2);
end;

procedure PtrInc(var P; Delta : Word);
  {-increase P by Delta}
begin
  Inc(PAnsiChar(P), Delta);
end;

procedure PtrDec(var P; Delta : Word);
  {-increase P by Delta}
begin
  Dec(PAnsiChar(P), Delta);
end;

{$IFDEF NoAsm}
function MinI(X, Y : Integer) : Integer;
begin
  if X < Y then
    Result := X
  else
    Result := Y;
end;

function MaxI(X, Y : Integer) : Integer;
begin
  if X >= Y then
    Result := X
  else
    Result := Y;
end;

{$ELSE}
function MinI(X, Y : Integer) : Integer;
asm
  cmp  eax, edx
  jle  @@Exit
  mov  eax, edx
@@Exit:
end;

function MaxI(X, Y : Integer) : Integer;
asm
  cmp  eax, edx
  jge  @@Exit
  mov  eax, edx
@@Exit:
end;
{$ENDIF}

function MaxL(A, B : LongInt) : LongInt;
begin
  if (A < B) then
    Result := B
  else
    Result := A;
end;

function MinL(A, B : LongInt) : LongInt;
begin
  if (A < B) then
    Result := A
  else
    Result := B;
end;

function TrimLeft(const S : string) : string;
var
  I, L : Integer;
begin
  L := Length(S);
  I := 1;
  while (I <= L) and (S[I] <= ' ') do
    Inc(I);
  Result := Copy(S, I, Length(S)-I+1);
end;

function TrimRight(const S : string) : string;
var
  I : Integer;
begin
  I := Length(S);
  while (I > 0) and (S[I] <= ' ') do
    Dec(I);
  Result := Copy(S, 1, I);
end;

function QuotedStr(const S: string): string;
var
  I : Integer;
begin
  Result := S;
  for I := Length(Result) downto 1 do
    if Result[I] = '''' then Insert('''', Result, I);
  Result := '''' + Result + '''';
end;

function WordCount(const S : string; const WordDelims : TCharSet) : Integer;
var
  SLen, I : Integer;
begin
  Result := 0;
  I := 1;
  SLen := Length(S);
  while I <= SLen do begin
    while (I <= SLen) and (S[I] in WordDelims) do
      Inc(I);
    if I <= SLen then
      Inc(Result);
    while (I <= SLen) and not(S[I] in WordDelims) do
      Inc(I);
  end;
end;

function ExtractWord(N : Integer; const S : string; WordDelims : TCharSet) : string;
var
  I   : Word;
  Len : Integer;
begin
  Len := 0;
  I := WordPosition(N, S, WordDelims);
  if I <> 0 then
    { find the end of the current word }
    while (I <= Length(S)) and not(S[I] in WordDelims) do begin
      { add the I'th character to result }
      Inc(Len);
      SetLength(Result, Len);
      Result[Len] := S[I];
      Inc(I);
    end;
  SetLength(Result, Len);
end;

function WordPosition(const N : Integer; const S : string; const WordDelims : TCharSet) : Integer;
var
  Count, I : Integer;
begin
  Count := 0;
  I := 1;
  Result := 0;
  while (I <= Length(S)) and (Count <> N) do begin
    {skip over delimiters}
    while (I <= Length(S)) and (S[I] in WordDelims) do
      Inc(I);
    {if we're not beyond end of S, we're at the start of a word}
    if I <= Length(S) then
      Inc(Count);
    {if not finished, find the end of the current word}
    if Count <> N then
      while (I <= Length(S)) and not (S[I] in WordDelims) do
        Inc(I)
    else
      Result := I;
  end;
end;

function DrawButtonFrame(Canvas : TCanvas; const Client : TRect;
  IsDown, IsFlat : Boolean; Style : TButtonStyle) : TRect;
var
  NewStyle : Boolean;
begin
  Result := Client;
  NewStyle := (Style = bsNew) or (NewStyleControls and (Style = bsAutoDetect));
  if IsDown then begin
    if NewStyle then begin
      Frame3D(Canvas, Result, clWindowFrame, clBtnHighlight, 1);
      if not IsFlat then
        Frame3D(Canvas, Result, clBtnShadow, clBtnFace, 1);
    end else begin
      if IsFlat then
        Frame3D(Canvas, Result, clWindowFrame, clBtnHighlight, 1)
      else begin
        Frame3D(Canvas, Result, clWindowFrame, clWindowFrame, 1);
        Canvas.Pen.Color := clBtnShadow;
        Canvas.PolyLine([Point(Result.Left, Result.Bottom - 1),
          Point(Result.Left, Result.Top), Point(Result.Right, Result.Top)]);
      end;
    end;
  end else begin
    if NewStyle then begin
      if IsFlat then
        Frame3D(Canvas, Result, clBtnHighlight, clBtnShadow, 1)
      else begin
        Frame3D(Canvas, Result, clBtnHighlight, clWindowFrame, 1);
        Frame3D(Canvas, Result, clBtnFace, clBtnShadow, 1);
      end;
    end else begin
      if IsFlat then
        Frame3D(Canvas, Result, clBtnHighlight, clWindowFrame, 1)
      else begin
        Frame3D(Canvas, Result, clWindowFrame, clWindowFrame, 1);
        Frame3D(Canvas, Result, clBtnHighlight, clBtnShadow, 1);
      end;
    end;
  end;
  InflateRect(Result, -1, -1);
end;

function GetDisplayString(Canvas : TCanvas; const S : string;
  MinChars, MaxWidth : Integer) : string;
var
  iDots, EllipsisWidth, Extent, Len, Width : Integer;
  ShowEllipsis : Boolean;
begin
  {be sure that the Canvas Font is set before entering this routine}
  EllipsisWidth := Canvas.TextWidth('...');
  Len := Length(S);
  Result := S;
  Extent := Canvas.TextWidth(Result);
  ShowEllipsis := False;
  Width := MaxWidth;
  while (Extent > Width) do begin
    ShowEllipsis := True;
    Width := MaxWidth - EllipsisWidth;
    if Len > MinChars then begin
      Delete(Result, Len, 1);
      dec(Len);
    end else
      break;
    Extent := Canvas.TextWidth(Result);
  end;
  if ShowEllipsis then begin
    Result := Result + '...';
    inc(Len, 3);
    Extent := Canvas.TextWidth(Result);
    iDots := 3;
    while (iDots > 0) and (Extent > MaxWidth) do begin
      Delete(Result, Len, 1);
      Dec(Len);
      Extent := Canvas.TextWidth(Result);
      Dec(iDots);
    end;
  end;
end;

type
  PCheckTaskInfo = ^TCheckTaskInfo;
  TCheckTaskInfo = packed record
    FocusWnd: HWnd;
    Found: Boolean;
  end;

{ - HWnd changed to TOvcHWnd for BCB Compatibility }
function CheckTaskWindow(Window: TOvcHWnd{HWnd};
  Data: Longint): WordBool; stdcall;
begin
  Result := True;
  if PCheckTaskInfo(Data)^.FocusWnd = Window then begin
    Result := False;
    PCheckTaskInfo(Data)^.Found := True;
  end;
end;

function IsForegroundTask : Boolean;
var
  Info : TCheckTaskInfo;
begin
  Info.FocusWnd := GetActiveWindow;
  Info.Found := False;
{$IFNDEF DARWIN}
  EnumThreadWindows(GetCurrentThreadID, @CheckTaskWindow, Longint(@Info));
{$ELSE}
  EnumThreadWindows(LongWord(GetCurrentThreadID), @CheckTaskWindow, Longint(@Info));
{$ENDIF}
  Result := Info.Found;
end;

procedure FixTextBuffer(InBuf, OutBuf : PChar; OutSize : Integer);
var
  I, P : Integer;
begin
  P := 0;
  for I := 0 to StrLen(InBuf) do begin
    if (InBuf[I] = #10) and ((I = 0) or (InBuf[I-1] <> #13)) then begin
      OutBuf[P] := #13;
      Inc(P);
    end;
    OutBuf[P] := InBuf[I];
    {is outbuf full?}
    if P = OutSize-1 then begin
      {if so, terminate and exit}
      OutBuf[OutSize] := #0;
      Break;
    end;
    Inc(P);
  end;
end;

{ - Hdc changed to TOvcHdc for BCB Compatibility }
procedure TransStretchBlt(DstDC: TOvcHdc{HDC}; DstX, DstY, DstW, DstH: Integer;
                           SrcDC: TOvcHdc{HDC}; SrcX, SrcY, SrcW, SrcH: Integer;
                           MaskDC: TOvcHdc{HDC};
                           MaskX, MaskY : Integer);
var
  MemDC : HDC;
  MemBmp : HBITMAP;
  Save : THandle;
  crText, crBack : TColorRef;
  SystemPalette16, SavePal : HPALETTE;
begin
  SavePal := 0;
  MemDC := CreateCompatibleDC(0);
  try
    MemBmp := CreateCompatibleBitmap(SrcDC, SrcW, SrcH);
    Save := SelectObject(MemDC, MemBmp);
    SystemPalette16 := GetStockObject(DEFAULT_PALETTE);
    SavePal := SelectPalette(SrcDC, SystemPalette16, False);
    SelectPalette(SrcDC, SavePal, False);
    if SavePal <> 0 then
      SavePal := SelectPalette(MemDC, SavePal, True)
    else
      SavePal := SelectPalette(MemDC, SystemPalette16, True);
    RealizePalette(MemDC);

    StretchBlt(MemDC, 0, 0, SrcW, SrcH, MaskDC, MaskX, MaskY,
                SrcW, SrcH, SrcCopy);
    StretchBlt(MemDC, 0, 0, SrcW, SrcH, SrcDC, SrcX, SrcY, SrcW, SrcH,
                SrcErase);
    crText := SetTextColor(DstDC, $0);
    crBack := SetBkColor(DstDC, $FFFFFF);
    StretchBlt(DstDC, DstX, DstY, DstW, DstH, MaskDC, MaskX, MaskY,
                SrcW, SrcH, SrcAnd);
    StretchBlt(DstDC, DstX, DstY, DstW, DstH, MemDC, 0, 0,
                SrcW, SrcH, SrcInvert);
    SetTextColor(DstDC, crText);
    SetBkColor(DstDC, crBack);
    if Save <> 0 then
      SelectObject(MemDC, Save);
    DeleteObject(MemBmp);
  finally
    if SavePal <> 0 then
      SelectPalette(MemDC, SavePal, False);
    DeleteDC(MemDC);
  end;
end;

function DefaultEpoch : Integer;
var
  ThisYear  : Word;
  ThisMonth : Word;
  ThisDay   : Word;
begin
  DecodeDate(SysUtils.Date, ThisYear, ThisMonth, ThisDay);
  Result := (ThisYear div 100) * 100;
end;

{function GenerateComponentName(PF : TCustomForm; const Root : string) : string;}
function GenerateComponentName(PF : TWinControl; const Root : string) : string;
var
  I : Integer;
begin
  if not IsValidIdent(Root) then
    raise EComponentError.CreateFmt('''''%s'''' is not a valid component name',
      [Root]);
  I := 0;
  repeat
    Inc(I);
    Result := Root + IntToStr(I);
  until (PF.FindComponent(Result) = nil);
end;

function PartialCompare(const S1, S2 : string) : Boolean;
var
  L : Integer;
begin
  {and empty string matches nothing}
  Result := False;
  L := MinI(Length(S1), Length(S2));
  if L > 0 then
    Result := AnsiUpperCase(Copy(S1, 1, L)) = AnsiUpperCase(Copy(S2, 1, L));
end;

function PathEllipsis(const S : string; MaxWidth : Integer) : string;
  { PathEllipsis function. Trims a path down to the      }
  { specified number of pixels. For example,             }
  { 'd:\program files\my stuff\some long document.txt'   }
  { becomes 'd:\...\some long...' or a variation thereof }
  { depending on the value of MaxWidth.                  }
var
  R       : TRect;
  BM      : TBitmap;
  NCM     : TNonClientMetrics;
begin
  if MaxWidth = 0 then begin
    Result := S;
    Exit;
  end;
  NCM.cbSize := SizeOf(NCM);
  SystemParametersInfo(
    SPI_GETNONCLIENTMETRICS, NCM.cbSize, @NCM, 0);
  BM := TBitmap.Create;
  try
    BM.Canvas.Font.Handle := CreateFontIndirect(NCM.lfMenuFont);
    if BM.Canvas.TextWidth(S) < MaxWidth then begin
      Result := S;
      Exit;
    end;
    Result := ExtractFilePath(S);
    Delete(Result, Length(Result), 1);
    while BM.Canvas.TextWidth(Result + '\...\' + ExtractFileName(S)) > MaxWidth do begin
      { Start trimming the path, working backwards }
      Result := ExtractFilePath(Result);
      Delete(Result, Length(Result), 1);
      { Only drive letter left so break out of loop. }
      if Length(Result) = 2 then
        Break;
    end;
    { Add the filename back onto the modified path. }
    Result := Result + '\...\' + ExtractFileName(S);
    { Still too long? }
    if BM.Canvas.TextWidth(Result) > MaxWidth then begin
      R := Rect(0, 0, MaxWidth, 0);
      DrawText(BM.Canvas.Handle, PChar(Result), -1,
        R, DT_SINGLELINE or DT_END_ELLIPSIS or DT_MODIFYSTRING or DT_CALCRECT);
    end;
  finally
    BM.Free;
  end;
end;

function CreateDisabledBitmap(FOriginal : TBitmap; OutlineColor : TColor) : TBitmap;
  {-create TBitmap object with disabled glyph}
const
  ROP_DSPDxax = $00E20746;
var
  MonoBmp : TBitmap;
  IRect   : TRect;
begin
  IRect := Rect(0, 0, FOriginal.Width, FOriginal.Height);
  Result := TBitmap.Create;
  try
    Result.Width := FOriginal.Width;
    Result.Height := FOriginal.Height;
    MonoBmp := TBitmap.Create;
    try
      with MonoBmp do begin
        Assign(FOriginal);
        HandleType := bmDDB;
        Canvas.Brush.Color := OutlineColor;
        if Monochrome then begin
          Canvas.Font.Color := clWhite;
          Monochrome := False;
          Canvas.Brush.Color := clWhite;
        end;
        Monochrome := True;
      end;
      with Result.Canvas do begin
        Brush.Color := clBtnFace;
        FillRect(IRect);
        Brush.Color := clBtnHighlight;
        SetTextColor(Handle, clBlack);
        SetBkColor(Handle, clWhite);
        BitBlt(Handle, 1, 1, WidthOf(IRect), HeightOf(IRect),
          MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
        Brush.Color := clBtnShadow;
        SetTextColor(Handle, clBlack);
        SetBkColor(Handle, clWhite);
        BitBlt(Handle, 0, 0, WidthOf(IRect), HeightOf(IRect),
          MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
      end;
    finally
      MonoBmp.Free;
    end;
  except
    Result.Free;
    raise;
  end;
end;

type
  TParentControl = class(TWinControl);

procedure CopyParentImage(Control : TControl; Dest : TCanvas);
var
  I     : Integer;
  Count : Integer;
  X, Y  : Integer;
  OldDC : Integer;
  DC    : hDC;
  R     : TRect;
  SelfR : TRect;
  CtlR  : TRect;
begin
  if Control.Parent = nil then
    Exit;

  Count := Control.Parent.ControlCount;
  DC := Dest.Handle;
  SelfR := Bounds(Control.Left, Control.Top, Control.Width, Control.Height);
  X := -Control.Left; Y := -Control.Top;

  {copy parent control image}
  OldDC := SaveDC(DC);
  SetViewportOrgEx(DC, X, Y, nil);
  IntersectClipRect(DC, 0, 0, Control.Parent.ClientWidth, Control.Parent.ClientHeight);
  TParentControl(Control.Parent).PaintWindow(DC);
  RestoreDC(DC, OldDC);

  {copy images of graphic controls}
  for I := 0 to Count - 1 do begin
    if (Control.Parent.Controls[I] <> nil) and
      not (Control.Parent.Controls[I] is TWinControl) then begin
      if Control.Parent.Controls[I] = Control then
        Break;
      with Control.Parent.Controls[I] do begin
        CtlR := Bounds(Left, Top, Width, Height);
        if Bool(IntersectRect(R, SelfR, CtlR)) and Visible then begin
          OldDC := SaveDC(DC);
          SetViewportOrgEx(DC, Left + X, Top + Y, nil);
          IntersectClipRect(DC, 0, 0, Width, Height);
          Perform(WM_PAINT, DC, 0);
          RestoreDC(DC, OldDC);
        end;
      end;
    end;
  end;
end;

{ - Hdc changed to TOvcHdc for BCB Compatibility }
procedure DrawTransparentBitmapPrim(DC : TOvcHdc{HDC}; Bitmap : HBitmap;
  xStart, yStart, Width, Height : Integer; Rect : TRect;
  TransparentColor : TColorRef);
  {-draw transparent bitmap}
var
{$IFNDEF LCL}
  BM          : Windows.TBitmap;
{$ELSE}
  BM          : LclType.tagBITMAP;
{$ENDIF}
  cColor      : TColorRef;
  bmAndBack   : hBitmap;
  bmAndObject : hBitmap;
  bmAndMem    : hBitmap;
  bmSave      : hBitmap;
  bmBackOld   : hBitmap;
  bmObjectOld : hBitmap;
  bmMemOld    : hBitmap;
  bmSaveOld   : hBitmap;
  hdcMem      : hDC;
  hdcBack     : hDC;
  hdcObject   : hDC;
  hdcTemp     : hDC;
  hdcSave     : hDC;
  ptSize      : TPoint;
  ptRealSize  : TPoint;
  ptBitSize   : TPoint;
  ptOrigin    : TPoint;
begin
  hdcTemp := CreateCompatibleDC(DC);
  SelectObject(hdcTemp, Bitmap);
  GetObject(Bitmap, SizeOf(BM), @BM);
  ptRealSize.x := MinL(Rect.Right - Rect.Left, BM.bmWidth - Rect.Left);
  ptRealSize.y := MinL(Rect.Bottom - Rect.Top, BM.bmHeight - Rect.Top);
  DPtoLP(hdcTemp, ptRealSize, 1);
  ptOrigin.x := Rect.Left;
  ptOrigin.y := Rect.Top;

  {convert from device to logical points}
  DPtoLP(hdcTemp, ptOrigin, 1);
  {get width of bitmap}
  ptBitSize.x := BM.bmWidth;
  {get height of bitmap}
  ptBitSize.y := BM.bmHeight;
  DPtoLP(hdcTemp, ptBitSize, 1);

  if (ptRealSize.x = 0) or (ptRealSize.y = 0) then begin
    ptSize := ptBitSize;
    ptRealSize := ptSize;
  end else
    ptSize := ptRealSize;
  if (Width = 0) or (Height = 0) then begin
    Width := ptSize.x;
    Height := ptSize.y;
  end;

  {create DCs to hold temporary data}
  hdcBack   := CreateCompatibleDC(DC);
  hdcObject := CreateCompatibleDC(DC);
  hdcMem    := CreateCompatibleDC(DC);
  hdcSave   := CreateCompatibleDC(DC);
  {create a bitmap for each DC}
  {monochrome DC}
  bmAndBack   := CreateBitmap(ptSize.x, ptSize.y, 1, 1, nil);
  bmAndObject := CreateBitmap(ptSize.x, ptSize.y, 1, 1, nil);
  bmAndMem    := CreateCompatibleBitmap(DC, MaxL(ptSize.x, Width), MaxL(ptSize.y, Height));
  bmSave      := CreateCompatibleBitmap(DC, ptBitSize.x, ptBitSize.y);
  {select a bitmap object to store pixel data}
  bmBackOld   := SelectObject(hdcBack, bmAndBack);
  bmObjectOld := SelectObject(hdcObject, bmAndObject);
  bmMemOld    := SelectObject(hdcMem, bmAndMem);
  bmSaveOld   := SelectObject(hdcSave, bmSave);

  SetMapMode(hdcTemp, GetMapMode(DC));

  {save the bitmap sent here, it will be overwritten}
  BitBlt(hdcSave, 0, 0, ptBitSize.x, ptBitSize.y, hdcTemp, 0, 0, SRCCOPY);

  {set the background color of the source DC to the color,}
  {contained in the parts of the bitmap that should be transparent}
  cColor := SetBkColor(hdcTemp, TransparentColor);

  {create the object mask for the bitmap by performing a BitBlt()}
  {from the source bitmap to a monochrome bitmap}
  BitBlt(hdcObject, 0, 0, ptSize.x, ptSize.y, hdcTemp, ptOrigin.x, ptOrigin.y, SRCCOPY);

  {set the background color of the source DC back to the original color}
  SetBkColor(hdcTemp, cColor);

  {create the inverse of the object mask}
  BitBlt(hdcBack, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0, NOTSRCCOPY);

  {copy the background of the main DC to the destination}
  BitBlt(hdcMem, 0, 0, Width, Height, DC, xStart, yStart, SRCCOPY);

  {mask out the places where the bitmap will be placed}
  StretchBlt(hdcMem, 0, 0, Width, Height, hdcObject, 0, 0, ptSize.x, ptSize.y, SRCAND);

  {mask out the transparent colored pixels on the bitmap}
  BitBlt(hdcTemp, ptOrigin.x, ptOrigin.y, ptSize.x, ptSize.y, hdcBack, 0, 0, SRCAND);

  {XOR the bitmap with the background on the destination DC}
  StretchBlt(hdcMem, 0, 0, Width, Height, hdcTemp, ptOrigin.x, ptOrigin.y, ptSize.x, ptSize.y, SRCPAINT);

  {copy the destination to the screen}
  BitBlt(DC, xStart, yStart, MaxL(ptRealSize.x, Width), MaxL(ptRealSize.y, Height), hdcMem, 0, 0, SRCCOPY);

  {place the original bitmap back into the bitmap sent}
  BitBlt(hdcTemp, 0, 0, ptBitSize.x, ptBitSize.y, hdcSave, 0, 0, SRCCOPY);

  {delete the memory bitmaps}
  DeleteObject(SelectObject(hdcBack, bmBackOld));
  DeleteObject(SelectObject(hdcObject, bmObjectOld));
  DeleteObject(SelectObject(hdcMem, bmMemOld));
  DeleteObject(SelectObject(hdcSave, bmSaveOld));

  {delete the memory DCs}
  DeleteDC(hdcMem);
  DeleteDC(hdcBack);
  DeleteDC(hdcObject);
  DeleteDC(hdcSave);
  DeleteDC(hdcTemp);
end;

procedure DrawTransparentBitmap(Dest : TCanvas; X, Y, W, H : Integer;
  Rect : TRect; Bitmap : TBitmap; TransparentColor : TColor);
var
  MemImage : TBitmap;
  R        : TRect;
begin
  MemImage := TBitmap.Create;
  try
    R := Bounds(0, 0, Bitmap.Width, Bitmap.Height);
    if TransparentColor = clNone then begin

      if (WidthOf(Rect) <> 0) and (HeightOf(Rect) <> 0) then
        R := Rect;
      MemImage.Width := WidthOf(R);
      MemImage.Height := HeightOf(R);
      MemImage.Canvas.CopyRect(Bounds(0, 0, MemImage.Width, MemImage.Height),
        Bitmap.Canvas, R);

      if (W = 0) or (H = 0) then
        Dest.Draw(X, Y, MemImage)
      else
        Dest.StretchDraw(Bounds(X, Y, W, H), MemImage);

    end else  begin
      MemImage.Width := WidthOf(R);
      MemImage.Height := HeightOf(R);
      MemImage.Canvas.CopyRect(R, Bitmap.Canvas, R);
      if TransparentColor = clDefault then
        TransparentColor := MemImage.Canvas.Pixels[0, MemImage.Height - 1];
      DrawTransparentBitmapPrim(Dest.Handle, MemImage.Handle, X, Y, W, H,
        Rect, ColorToRGB(TransparentColor and not $02000000));
    end;
  finally
    MemImage.Free;
  end;
end;


function WidthOf(const R : TRect) : Integer;
begin
  Result := R.Right - R.Left;
end;

function HeightOf(const R : TRect) : Integer;
begin
  Result := R.Bottom - R.Top;
end;

procedure DebugOutput(const S : string);
begin
  OutputDebugString(PChar(S));
  OutputDebugString(#13#10);
end;


end.
