| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023 | 
							- unit TB2Common;
 
- {
 
-   Toolbar2000
 
-   Copyright (C) 1998-2005 by Jordan Russell
 
-   All rights reserved.
 
-   The contents of this file are subject to the "Toolbar2000 License"; you may
 
-   not use or distribute this file except in compliance with the
 
-   "Toolbar2000 License". A copy of the "Toolbar2000 License" may be found in
 
-   TB2k-LICENSE.txt or at:
 
-     http://www.jrsoftware.org/files/tb2k/TB2k-LICENSE.txt
 
-   Alternatively, the contents of this file may be used under the terms of the
 
-   GNU General Public License (the "GPL"), in which case the provisions of the
 
-   GPL are applicable instead of those in the "Toolbar2000 License". A copy of
 
-   the GPL may be found in GPL-LICENSE.txt or at:
 
-     http://www.jrsoftware.org/files/tb2k/GPL-LICENSE.txt
 
-   If you wish to allow use of your version of this file only under the terms of
 
-   the GPL and not to allow others to use your version of this file under the
 
-   "Toolbar2000 License", indicate your decision by deleting the provisions
 
-   above and replace them with the notice and other provisions required by the
 
-   GPL. If you do not delete the provisions above, a recipient may use your
 
-   version of this file under either the "Toolbar2000 License" or the GPL.
 
-   $jrsoftware: tb2k/Source/TB2Common.pas,v 1.31 2005/06/29 20:10:10 jr Exp $
 
- }
 
- interface
 
- {$I TB2Ver.inc}
 
- uses
 
-   Windows, Classes, SysUtils, Messages, Controls, Forms;
 
- type
 
-   TListSortExCompare = function(const Item1, Item2, ExtraData: Pointer): Integer;
 
-   THandleWMPrintNCPaintProc = procedure(Control: TControl; Wnd: HWND; DC: HDC; AppData: Longint);
 
- function AddToFrontOfList(var List: TList; Item: Pointer): Boolean;
 
- function AddToList(var List: TList; Item: Pointer): Boolean;
 
- function ApplicationIsActive: Boolean;
 
- function AreFlatMenusEnabled: Boolean;
 
- function AreKeyboardCuesEnabled: Boolean;
 
- function CallTrackMouseEvent(const Wnd: HWND; const Flags: DWORD): Boolean;
 
- function CreateHalftoneBrush: HBRUSH;
 
- function CreateNullRegion: HRGN;
 
- function CreateRotatedFont(DC: HDC): HFONT;
 
- function DivRoundUp(const Dividend, Divisor: Integer): Integer;
 
- procedure DrawHalftoneInvertRect(const DC: HDC; const NewRect, OldRect: PRect;
 
-   const NewSize, OldSize: TSize);
 
- procedure DrawRotatedText(const DC: HDC; AText: String; const ARect: TRect;
 
-   const AFormat: Cardinal);
 
- procedure DrawInvertRect(const DC: HDC; const NewRect, OldRect: PRect;
 
-   const NewSize, OldSize: TSize; const Brush: HBRUSH; BrushLast: HBRUSH);
 
- function EscapeAmpersands(const S: String): String;
 
- function FindAccelChar(const S: String): Char;
 
- function GetInputLocaleCodePage: UINT;
 
- function GetMenuShowDelay: Integer;
 
- function GetRectOfMonitorContainingPoint(const P: TPoint; const WorkArea: Boolean): TRect;
 
- function GetRectOfMonitorContainingRect(const R: TRect; const WorkArea: Boolean): TRect;
 
- function GetRectOfMonitorContainingWindow(const W: HWND; const WorkArea: Boolean): TRect;
 
- function GetRectOfPrimaryMonitor(const WorkArea: Boolean): TRect;
 
- function GetTextHeight(const DC: HDC): Integer;
 
- function GetTextWidth(const DC: HDC; S: String; const Prefix: Boolean): Integer;
 
- procedure HandleWMPrint(Control: TControl; const Wnd: HWND; var Message: TMessage;
 
-   const NCPaintFunc: THandleWMPrintNCPaintProc; const AppData: Longint);
 
- procedure HandleWMPrintClient(const Control: TWinControl;
 
-   var Message: TMessage);
 
- procedure ListSortEx(const List: TList; const Compare: TListSortExCompare;
 
-   const ExtraData: Pointer);
 
- function Max(A, B: Integer): Integer;
 
- function Min(A, B: Integer): Integer;
 
- function MethodsEqual(const M1, M2: TMethod): Boolean;
 
- function NeedToPlaySound(const Alias: String): Boolean;
 
- procedure ProcessPaintMessages;
 
- procedure RemoveMessages(const AMin, AMax: Integer);
 
- procedure RemoveFromList(var List: TList; Item: Pointer);
 
- procedure SelectNCUpdateRgn(Wnd: HWND; DC: HDC; Rgn: HRGN);
 
- function StripAccelChars(const S: String; IncludingStandaloneKey: Boolean = False {MP}): String;
 
- function StripTrailingPunctuation(const S: String): String;
 
- function UsingMultipleMonitors: Boolean;
 
- const
 
-   PopupMenuWindowNCSize = 3;
 
-   DT_HIDEPREFIX = $00100000;{$EXTERNALSYM DT_HIDEPREFIX}
 
- var
 
-   TrackMouseEventFunc: function(var EventTrack: TTrackMouseEvent): BOOL; stdcall;
 
- implementation
 
- uses
 
-   TB2Version, Types, System.Character {MP};
 
- function ApplicationIsActive: Boolean;
 
- { Returns True if the application is in the foreground }
 
- begin
 
-   Result := GetActiveWindow <> 0;
 
- end;
 
- procedure ListSortEx(const List: TList; const Compare: TListSortExCompare;
 
-   const ExtraData: Pointer);
 
- { Similar to TList.Sort, but lets you pass a user-defined ExtraData pointer }
 
-   procedure QuickSortEx(L: Integer; const R: Integer);
 
-   var
 
-     I, J: Integer;
 
-     P: Pointer;
 
-   begin
 
-     repeat
 
-       I := L;
 
-       J := R;
 
-       P := List[(L + R) shr 1];
 
-       repeat
 
-         while Compare(List[I], P, ExtraData) < 0 do Inc(I);
 
-         while Compare(List[J], P, ExtraData) > 0 do Dec(J);
 
-         if I <= J then
 
-         begin
 
-           List.Exchange(I, J);
 
-           Inc(I);
 
-           Dec(J);
 
-         end;
 
-       until I > J;
 
-       if L < J then QuickSortEx(L, J);
 
-       L := I;
 
-     until I >= R;
 
-   end;
 
- begin
 
-   if List.Count > 1 then
 
-     QuickSortEx(0, List.Count-1);
 
- end;
 
- type
 
-   PPrintEnumProcData = ^TPrintEnumProcData;
 
-   TPrintEnumProcData = record
 
-     PrintChildren: Boolean;
 
-     ParentWnd: HWND;
 
-     DC: HDC;
 
-     PrintFlags: LPARAM;
 
-   end;
 
- function PrintEnumProc(Wnd: HWND; LParam: LPARAM): BOOL; stdcall;
 
- var
 
-   R: TRect;
 
-   SaveIndex: Integer;
 
- begin
 
-   Result := True;  { continue enumerating }
 
-   with PPrintEnumProcData(LParam)^ do begin
 
-     { Skip window if it isn't a child/owned window of ParentWnd or isn't visible }
 
-     if (HWND(GetWindowLong(Wnd, GWL_HWNDPARENT)) <> ParentWnd) or
 
-        (GetWindowLong(Wnd, GWL_STYLE) and WS_VISIBLE = 0) then
 
-          { ^ don't use IsWindowVisible since it returns False if the window's
 
-            parent window is not visible }
 
-       Exit;
 
-     GetWindowRect(Wnd, R);
 
-     MapWindowPoints(0, ParentWnd, R, 2);
 
-     SaveIndex := SaveDC(DC);
 
-     { Like Windows, offset the window origin to the top-left coordinates of
 
-       the child/owned window }
 
-     MoveWindowOrg(DC, R.Left, R.Top);
 
-     { Like Windows, intersect the clipping region with the entire rectangle of
 
-       the child/owned window }
 
-     OffsetRect(R, -R.Left, -R.Top);
 
-     IntersectClipRect(DC, R.Left, R.Top, R.Right, R.Bottom);
 
-     { Send a WM_PRINT message to the child/owned window }
 
-     SendMessage(Wnd, WM_PRINT, WPARAM(DC), PrintFlags);
 
-     { Restore the DC's state, in case the WM_PRINT handler didn't put things
 
-       back the way it found them }
 
-     RestoreDC(DC, SaveIndex);
 
-   end;
 
- end;
 
- procedure HandleWMPrint(Control: TControl; const Wnd: HWND; var Message: TMessage;
 
-   const NCPaintFunc: THandleWMPrintNCPaintProc; const AppData: Longint);
 
- { note: AppData is an application-defined value which is passed to NCPaintFunc }
 
- var
 
-   DC: HDC;
 
-   SaveIndex, SaveIndex2: Integer;
 
-   R: TRect;
 
-   P: TPoint;
 
-   Data: TPrintEnumProcData;
 
- begin
 
-   if (Message.LParam and PRF_CHECKVISIBLE = 0) or IsWindowVisible(Wnd) then begin
 
-     DC := HDC(Message.WParam);
 
-     SaveIndex2 := SaveDC(DC);
 
-     try
 
-       if Message.LParam and PRF_NONCLIENT <> 0 then begin
 
-         SaveIndex := SaveDC(DC);
 
-         if Assigned(NCPaintFunc) then
 
-           NCPaintFunc(Control, Wnd, DC, AppData);
 
-         RestoreDC(DC, SaveIndex);
 
-       end;
 
-       { Calculate the difference between the top-left corner of the window
 
-         and the top-left corner of its client area }
 
-       GetWindowRect(Wnd, R);
 
-       P.X := 0;  P.Y := 0;
 
-       ClientToScreen(Wnd, P);
 
-       Dec(P.X, R.Left);  Dec(P.Y, R.Top);
 
-       if Message.LParam and PRF_CLIENT <> 0 then begin
 
-         { Like Windows, the flags PRF_ERASEBKGND, PRF_CHILDREN, and PRF_OWNED
 
-           are ignored if PRF_CLIENT isn't also specified }
 
-         if Message.LParam and PRF_ERASEBKGND <> 0 then begin
 
-           { Send WM_ERASEBKGND }
 
-           SaveIndex := SaveDC(DC);
 
-           if Message.LParam and PRF_NONCLIENT <> 0 then
 
-             MoveWindowOrg(DC, P.X, P.Y);
 
-           SendMessage(Wnd, WM_ERASEBKGND, Message.WParam, 0);
 
-           RestoreDC(DC, SaveIndex);
 
-         end;
 
-         { Send WM_PRINTCLIENT }
 
-         SaveIndex := SaveDC(DC);
 
-         if Message.LParam and PRF_NONCLIENT <> 0 then
 
-           MoveWindowOrg(DC, P.X, P.Y);
 
-         SendMessage(Wnd, WM_PRINTCLIENT, Message.WParam, 0);
 
-         RestoreDC(DC, SaveIndex);
 
-         { Like Windows, always offset child/owned windows by the size of the
 
-           client area even if PRF_NONCLIENT isn't specified (a bug?) }
 
-         MoveWindowOrg(DC, P.X, P.Y);
 
-         Data.ParentWnd := Wnd;
 
-         Data.DC := DC;
 
-         { Send WM_PRINT to child/owned windows }
 
-         if Message.LParam and PRF_CHILDREN <> 0 then begin
 
-           Data.PrintChildren := True;
 
-           Data.PrintFlags := PRF_NONCLIENT or PRF_CLIENT or PRF_ERASEBKGND or
 
-             PRF_CHILDREN;  { same flags as Windows passes to children }
 
-           EnumChildWindows(Wnd, @PrintEnumProc, LPARAM(@Data));
 
-         end;
 
-         if Message.LParam and PRF_OWNED <> 0 then begin
 
-           Data.PrintChildren := False;
 
-           Data.PrintFlags := Message.LParam;
 
-           EnumWindows(@PrintEnumProc, LPARAM(@Data));
 
-         end;
 
-       end;
 
-     finally
 
-       RestoreDC(DC, SaveIndex2);
 
-     end;
 
-   end;
 
-   { Windows' WM_PRINT returns 1. I'm not sure why. }
 
-   Message.Result := 1;
 
- end;
 
- type
 
-   TWinControlAccess = class(TWinControl);
 
- procedure HandleWMPrintClient(const Control: TWinControl; var Message: TMessage);
 
- var
 
-   Msg: TWMPaint;
 
-   SaveIndex: Integer;
 
- begin
 
-   Msg.Msg := WM_PAINT;
 
-   Msg.DC := HDC(Message.WParam);
 
-   Msg.Unused := 0;
 
-   Msg.Result := 0;
 
-   SaveIndex := SaveDC(HDC(Message.WParam));
 
-   try
 
-     TWinControlAccess(Control).PaintHandler(Msg);
 
-   finally
 
-     RestoreDC(HDC(Message.WParam), SaveIndex);
 
-   end;
 
- end;
 
- function DivRoundUp(const Dividend, Divisor: Integer): Integer;
 
- { Similar to the 'div' operator, but if there is a remainder it always rounds
 
-   the result up one (or down if the result is negative). }
 
- asm
 
-   mov  ecx, edx
 
-   cdq
 
-   idiv ecx
 
-   test edx, edx
 
-   jz   @@1
 
-   test eax, eax
 
-   jns  @@2
 
-   dec  eax
 
-   jmp  @@1
 
-   @@2:
 
-   inc  eax
 
-   @@1:
 
- end;
 
- function GetTextHeight(const DC: HDC): Integer;
 
- var
 
-   TextMetric: TTextMetric;
 
- begin
 
-   GetTextMetrics(DC, TextMetric);
 
-   Result := TextMetric.tmHeight;
 
- end;
 
- function StripAccelChars(const S: String; IncludingStandaloneKey: Boolean {MP}): String;
 
- var
 
-   I: Integer;
 
- begin
 
-   Result := S;
 
-   I := 1;
 
-   while I <= Length(Result) do begin
 
-     if not CharInSet(Result[I], LeadBytes) then begin
 
-       if Result[I] = '&' then
 
-       begin
 
-         {MP}
 
-         // Trim trailing artificial accelerators typical for asian translation
 
-         // e.g. "ローカル(&L)"
 
-         if IncludingStandaloneKey and
 
-            (I = Length(Result) - 2) and
 
-            (Result[I - 1] = '(') and
 
-            Result[I + 1].IsLetter() and
 
-            (Result[I + 2] = ')') then
 
-         begin
 
-           System.Delete(Result, I - 1, 4);
 
-         end
 
-           else
 
-         begin
 
-           System.Delete(Result, I, 1);
 
-         end;
 
-       end;
 
-       Inc(I);
 
-     end
 
-     else
 
-       Inc(I, 2);
 
-   end;
 
- end;
 
- function EscapeAmpersands(const S: String): String;
 
- { Replaces any '&' characters with '&&' }
 
- var
 
-   I: Integer;
 
- begin
 
-   Result := S;
 
-   I := 1;
 
-   while I <= Length(Result) do begin
 
-     if not CharInSet(Result[I], LeadBytes) then begin
 
-       if Result[I] = '&' then begin
 
-         Inc(I);
 
-         Insert('&', Result, I);
 
-       end;
 
-       Inc(I);
 
-     end
 
-     else
 
-       Inc(I, 2);
 
-   end;
 
- end;
 
- function StripTrailingPunctuation(const S: String): String;
 
- { Removes any colon (':') or ellipsis ('...') from the end of S and returns
 
-   the resulting string }
 
- var
 
-   L: Integer;
 
- begin
 
-   Result := S;
 
-   L := Length(Result);
 
-   if (L > 1) and (Result[L] = ':') and (ByteType(Result, L) = mbSingleByte) then
 
-     SetLength(Result, L-1)
 
-   else if (L > 3) and (Result[L-2] = '.') and (Result[L-1] = '.') and
 
-      (Result[L] = '.') and (ByteType(Result, L-2) = mbSingleByte) then
 
-     SetLength(Result, L-3);
 
- end;
 
- function GetTextWidth(const DC: HDC; S: String; const Prefix: Boolean): Integer;
 
- { Returns the width of the specified string using the font currently selected
 
-   into DC. If Prefix is True, it first removes "&" characters as necessary. }
 
- var
 
-   Size: TSize;
 
- begin
 
-   { This procedure is 10x faster than using DrawText with the DT_CALCRECT flag }
 
-   if Prefix then
 
-     S := StripAccelChars(S);
 
-   GetTextExtentPoint32(DC, PChar(S), Length(S), Size);
 
-   Result := Size.cx;
 
- end;
 
- procedure ProcessPaintMessages;
 
- { Dispatches all pending WM_PAINT messages. In effect, this is like an
 
-   'UpdateWindow' on all visible windows }
 
- var
 
-   Msg: TMsg;
 
- begin
 
-   while PeekMessage(Msg, 0, WM_PAINT, WM_PAINT, PM_NOREMOVE) do begin
 
-     case Integer(GetMessage(Msg, 0, WM_PAINT, WM_PAINT)) of
 
-       -1: Break; { if GetMessage failed }
 
-       0: begin
 
-            { Repost WM_QUIT messages }
 
-            PostQuitMessage(Msg.WParam);
 
-            Break;
 
-          end;
 
-     end;
 
-     DispatchMessage(Msg);
 
-   end;
 
- end;
 
- procedure RemoveMessages(const AMin, AMax: Integer);
 
- { Removes any messages with the specified ID from the queue }
 
- var
 
-   Msg: TMsg;
 
- begin
 
-   while PeekMessage(Msg, 0, AMin, AMax, PM_REMOVE) do begin
 
-     if Msg.message = WM_QUIT then begin
 
-       { Repost WM_QUIT messages }
 
-       PostQuitMessage(Msg.WParam);
 
-       Break;
 
-     end;
 
-   end;
 
- end;
 
- procedure SelectNCUpdateRgn(Wnd: HWND; DC: HDC; Rgn: HRGN);
 
- var
 
-   R: TRect;
 
-   NewClipRgn: HRGN;
 
- begin
 
-   if (Rgn <> 0) and (Rgn <> 1) then begin
 
-     GetWindowRect(Wnd, R);
 
-     if SelectClipRgn(DC, Rgn) = ERROR then begin
 
-       NewClipRgn := CreateRectRgnIndirect(R);
 
-       SelectClipRgn(DC, NewClipRgn);
 
-       DeleteObject(NewClipRgn);
 
-     end;
 
-     OffsetClipRgn(DC, -R.Left, -R.Top);
 
-   end;
 
- end;
 
- function AddToList(var List: TList; Item: Pointer): Boolean;
 
- { Returns True if Item didn't already exist in the list }
 
- begin
 
-   if List = nil then
 
-     List := TList.Create;
 
-   Result := List.IndexOf(Item) = -1;
 
-   if Result then
 
-     List.Add(Item);
 
- end;
 
- function AddToFrontOfList(var List: TList; Item: Pointer): Boolean;
 
- { Returns True if Item didn't already exist in the list }
 
- begin
 
-   if List = nil then
 
-     List := TList.Create;
 
-   Result := List.IndexOf(Item) = -1;
 
-   if Result then
 
-     List.Insert(0, Item);
 
- end;
 
- procedure RemoveFromList(var List: TList; Item: Pointer);
 
- begin
 
-   if Assigned(List) then begin
 
-     List.Remove(Item);
 
-     if List.Count = 0 then begin
 
-       List.Free;
 
-       List := nil;
 
-     end;
 
-   end;
 
- end;
 
- var
 
-   RegMenuShowDelay: Integer;
 
-   RegMenuShowDelayInited: BOOL = False;
 
- function GetMenuShowDelay: Integer;
 
- const
 
-   DefaultMenuShowDelay = 400;
 
-   function ReadMenuShowDelayFromRegistry: Integer;
 
-   var
 
-     K: HKEY;
 
-     Typ, DataSize: DWORD;
 
-     Data: array[0..31] of Char;
 
-     Res: Longint;
 
-     E: Integer;
 
-   begin
 
-     Result := DefaultMenuShowDelay;
 
-     if RegOpenKeyEx(HKEY_CURRENT_USER, 'Control Panel\Desktop', 0,
 
-        KEY_QUERY_VALUE, K) = ERROR_SUCCESS then begin
 
-       DataSize := SizeOf(Data);
 
-       Res := RegQueryValueEx(K, 'MenuShowDelay', nil, @Typ, @Data, @DataSize);
 
-       RegCloseKey(K);
 
-       if Res <> ERROR_FILE_NOT_FOUND then begin
 
-         if (Res <> ERROR_SUCCESS) or (Typ <> REG_SZ) then
 
-           Result := 0
 
-         else begin
 
-           Val(Data, Result, E);
 
-           if E <> 0 then Result := 0;
 
-         end;
 
-       end;
 
-     end;
 
-   end;
 
- begin
 
-   if Lo(GetVersion) >= 4 then begin
 
-     if not SystemParametersInfo(106{SPI_GETMENUSHOWDELAY}, 0, @Result, 0) then begin
 
-       { SPI_GETMENUSHOWDELAY is only supported by Windows NT 4.0 and Windows 98.
 
-         On Windows 95, it must use the registry to retrieve this setting. }
 
-       if not RegMenuShowDelayInited then begin
 
-         RegMenuShowDelay := ReadMenuShowDelayFromRegistry;
 
-         InterlockedExchange(Integer(RegMenuShowDelayInited), Ord(True));
 
-       end;
 
-       Result := RegMenuShowDelay;
 
-     end;
 
-     if Result < 0 then Result := 0;
 
-   end
 
-   else
 
-     Result := DefaultMenuShowDelay;
 
- end;
 
- function AreFlatMenusEnabled: Boolean;
 
- { Returns True if "flat menus" are enabled. Always returns False on pre-XP
 
-   Windows versions. }
 
- const
 
-   SPI_GETFLATMENU = $1022;
 
- var
 
-   FlatMenusEnabled: BOOL;
 
- begin
 
-   Result := SystemParametersInfo(SPI_GETFLATMENU, 0,
 
-     @FlatMenusEnabled, 0) and FlatMenusEnabled;
 
- end;
 
- function AreKeyboardCuesEnabled: Boolean;
 
- { Returns True if "keyboard cues" are enabled. Always returns True on
 
-   pre-2000 Windows versions. }
 
- const
 
-   SPI_GETKEYBOARDCUES = $100A;
 
- var
 
-   CuesEnabled: BOOL;
 
- begin
 
-   Result :=
 
-     not SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, @CuesEnabled, 0) or
 
-     CuesEnabled;
 
- end;
 
- function CreateNullRegion: HRGN;
 
- var
 
-   R: TRect;
 
- begin
 
-   SetRectEmpty(R);
 
-   Result := CreateRectRgnIndirect(R);
 
- end;
 
- procedure DrawInvertRect(const DC: HDC; const NewRect, OldRect: PRect;
 
-   const NewSize, OldSize: TSize; const Brush: HBRUSH; BrushLast: HBRUSH);
 
- { Draws a dragging outline, hiding the old one if neccessary. This code is
 
-   based on MFC sources.
 
-   Either NewRect or OldRect can be nil or empty. }
 
- var
 
-   SaveIndex: Integer;
 
-   rgnNew, rgnOutside, rgnInside, rgnLast, rgnUpdate: HRGN;
 
-   R: TRect;
 
- begin
 
-   rgnLast := 0;
 
-   rgnUpdate := 0;
 
-   { First, determine the update region and select it }
 
-   if NewRect = nil then begin
 
-     SetRectEmpty(R);
 
-     rgnOutside := CreateRectRgnIndirect(R);
 
-   end
 
-   else begin
 
-     R := NewRect^;
 
-     rgnOutside := CreateRectRgnIndirect(R);
 
-     InflateRect(R, -NewSize.cx, -NewSize.cy);
 
-     IntersectRect(R, R, NewRect^);
 
-   end;
 
-   rgnInside := CreateRectRgnIndirect(R);
 
-   rgnNew := CreateNullRegion;
 
-   CombineRgn(rgnNew, rgnOutside, rgnInside, RGN_XOR);
 
-   if BrushLast = 0 then
 
-     BrushLast := Brush;
 
-   if OldRect <> nil then begin
 
-     { Find difference between new region and old region }
 
-     rgnLast := CreateNullRegion;
 
-     with OldRect^ do
 
-       SetRectRgn(rgnOutside, Left, Top, Right, Bottom);
 
-     R := OldRect^;
 
-     InflateRect(R, -OldSize.cx, -OldSize.cy);
 
-     IntersectRect(R, R, OldRect^);
 
-     SetRectRgn(rgnInside, R.Left, R.Top, R.Right, R.Bottom);
 
-     CombineRgn(rgnLast, rgnOutside, rgnInside, RGN_XOR);
 
-     { Only diff them if brushes are the same }
 
-     if Brush = BrushLast then begin
 
-       rgnUpdate := CreateNullRegion;
 
-       CombineRgn(rgnUpdate, rgnLast, rgnNew, RGN_XOR);
 
-     end;
 
-   end;
 
-   { Save the DC state so that the clipping region can be restored }
 
-   SaveIndex := SaveDC(DC);
 
-   try
 
-     if (Brush <> BrushLast) and (OldRect <> nil) then begin
 
-       { Brushes are different -- erase old region first }
 
-       SelectClipRgn(DC, rgnLast);
 
-       GetClipBox(DC, R);
 
-       SelectObject(DC, BrushLast);
 
-       PatBlt(DC, R.Left, R.Top, R.Right-R.Left, R.Bottom-R.Top, PATINVERT);
 
-     end;
 
-     { Draw into the update/new region }
 
-     if rgnUpdate <> 0 then
 
-       SelectClipRgn(DC, rgnUpdate)
 
-     else
 
-       SelectClipRgn(DC, rgnNew);
 
-     GetClipBox(DC, R);
 
-     SelectObject(DC, Brush);
 
-     PatBlt(DC, R.Left, R.Top, R.Right-R.Left, R.Bottom-R.Top, PATINVERT);
 
-   finally
 
-     { Clean up DC }
 
-     RestoreDC(DC, SaveIndex);
 
-   end;
 
-   { Free regions }
 
-   if rgnNew <> 0 then DeleteObject(rgnNew);
 
-   if rgnOutside <> 0 then DeleteObject(rgnOutside);
 
-   if rgnInside <> 0 then DeleteObject(rgnInside);
 
-   if rgnLast <> 0 then DeleteObject(rgnLast);
 
-   if rgnUpdate <> 0 then DeleteObject(rgnUpdate);
 
- end;
 
- function CreateHalftoneBrush: HBRUSH;
 
- const
 
-   Patterns: array[Boolean] of Word = ($5555, $AAAA);
 
- var
 
-   I: Integer;
 
-   GrayPattern: array[0..7] of Word;
 
-   GrayBitmap: HBITMAP;
 
- begin
 
-   for I := 0 to 7 do
 
-     GrayPattern[I] := Patterns[Odd(I)];
 
-   GrayBitmap := CreateBitmap(8, 8, 1, 1, @GrayPattern);
 
-   Result := CreatePatternBrush(GrayBitmap);
 
-   DeleteObject(GrayBitmap);
 
- end;
 
- procedure DrawHalftoneInvertRect(const DC: HDC; const NewRect, OldRect: PRect;
 
-   const NewSize, OldSize: TSize);
 
- var
 
-   Brush: HBRUSH;
 
- begin
 
-   Brush := CreateHalftoneBrush;
 
-   try
 
-     DrawInvertRect(DC, NewRect, OldRect, NewSize, OldSize, Brush, Brush);
 
-   finally
 
-     DeleteObject(Brush);
 
-   end;
 
- end;
 
- function MethodsEqual(const M1, M2: TMethod): Boolean;
 
- begin
 
-   Result := (M1.Code = M2.Code) and (M1.Data = M2.Data);
 
- end;
 
- function GetRectOfPrimaryMonitor(const WorkArea: Boolean): TRect;
 
- begin
 
-   if not WorkArea or not SystemParametersInfo(SPI_GETWORKAREA, 0, @Result, 0) then
 
-     Result := Rect(0, 0, Screen.Width, Screen.Height);
 
- end;
 
- function UsingMultipleMonitors: Boolean;
 
- { Returns True if the system has more than one display monitor configured. }
 
- var
 
-   NumMonitors: Integer;
 
- begin
 
-   NumMonitors := GetSystemMetrics(80 {SM_CMONITORS});
 
-   Result := (NumMonitors <> 0) and (NumMonitors <> 1);
 
-   { ^ NumMonitors will be zero if not running Win98, NT 5, or later }
 
- end;
 
- type
 
-   HMONITOR = type Integer;
 
-   PMonitorInfo = ^TMonitorInfo;
 
-   TMonitorInfo = record
 
-     cbSize: DWORD;
 
-     rcMonitor: TRect;
 
-     rcWork: TRect;
 
-     dwFlags: DWORD;
 
-   end;
 
- const
 
-   MONITOR_DEFAULTTONEAREST = $2;
 
- type
 
-   TMultiMonApis = record
 
-     funcMonitorFromRect: function(lprcScreenCoords: PRect; dwFlags: DWORD): HMONITOR; stdcall;
 
-     funcMonitorFromPoint: function(ptScreenCoords: TPoint; dwFlags: DWORD): HMONITOR; stdcall;
 
-     funcMonitorFromWindow: function(hWnd: HWND; dwFlags: DWORD): HMONITOR; stdcall;
 
-     funcGetMonitorInfoW: function(hMonitor: HMONITOR; lpMonitorInfo: PMonitorInfo): BOOL; stdcall;
 
-   end;
 
- { Under D4 I could be using the MultiMon unit for the multiple monitor
 
-   function imports, but its stubs for MonitorFromRect and MonitorFromPoint
 
-   are seriously bugged... So I chose to avoid the MultiMon unit entirely. }
 
- function InitMultiMonApis(var Apis: TMultiMonApis): Boolean;
 
- var
 
-   User32Handle: THandle;
 
- begin
 
-   User32Handle := GetModuleHandle(user32);
 
-   Apis.funcMonitorFromRect := GetProcAddress(User32Handle, 'MonitorFromRect');
 
-   Apis.funcMonitorFromPoint := GetProcAddress(User32Handle, 'MonitorFromPoint');
 
-   Apis.funcMonitorFromWindow := GetProcAddress(User32Handle, 'MonitorFromWindow');
 
-   Apis.funcGetMonitorInfoW := GetProcAddress(User32Handle, 'GetMonitorInfoW');
 
-   Result := Assigned(Apis.funcMonitorFromRect) and
 
-     Assigned(Apis.funcMonitorFromPoint) and Assigned(Apis.funcGetMonitorInfoW);
 
- end;
 
- function GetRectOfMonitorContainingRect(const R: TRect;
 
-   const WorkArea: Boolean): TRect;
 
- { Returns the work area of the monitor which the rectangle R intersects with
 
-   the most, or the monitor nearest R if no monitors intersect. }
 
- var
 
-   Apis: TMultiMonApis;
 
-   M: HMONITOR;
 
-   MonitorInfo: TMonitorInfo;
 
- begin
 
-   if UsingMultipleMonitors and InitMultiMonApis(Apis) then begin
 
-     M := Apis.funcMonitorFromRect(@R, MONITOR_DEFAULTTONEAREST);
 
-     MonitorInfo.cbSize := SizeOf(MonitorInfo);
 
-     if Apis.funcGetMonitorInfoW(M, @MonitorInfo) then begin
 
-       if not WorkArea then
 
-         Result := MonitorInfo.rcMonitor
 
-       else
 
-         Result := MonitorInfo.rcWork;
 
-       Exit;
 
-     end;
 
-   end;
 
-   Result := GetRectOfPrimaryMonitor(WorkArea);
 
- end;
 
- function GetRectOfMonitorContainingPoint(const P: TPoint;
 
-   const WorkArea: Boolean): TRect;
 
- { Returns the screen area of the monitor containing the point P, or the monitor
 
-   nearest P if P isn't in any monitor's work area. }
 
- var
 
-   Apis: TMultiMonApis;
 
-   M: HMONITOR;
 
-   MonitorInfo: TMonitorInfo;
 
- begin
 
-   if UsingMultipleMonitors and InitMultiMonApis(Apis) then begin
 
-     M := Apis.funcMonitorFromPoint(P, MONITOR_DEFAULTTONEAREST);
 
-     MonitorInfo.cbSize := SizeOf(MonitorInfo);
 
-     if Apis.funcGetMonitorInfoW(M, @MonitorInfo) then begin
 
-       if not WorkArea then
 
-         Result := MonitorInfo.rcMonitor
 
-       else
 
-         Result := MonitorInfo.rcWork;
 
-       Exit;
 
-     end;
 
-   end;
 
-   Result := GetRectOfPrimaryMonitor(WorkArea);
 
- end;
 
- function GetRectOfMonitorContainingWindow(const W: HWND;
 
-   const WorkArea: Boolean): TRect;
 
- var
 
-   Apis: TMultiMonApis;
 
-   M: HMONITOR;
 
-   MonitorInfo: TMonitorInfo;
 
- begin
 
-   if UsingMultipleMonitors and InitMultiMonApis(Apis) then begin
 
-     M := Apis.funcMonitorFromWindow(W, MONITOR_DEFAULTTONEAREST);
 
-     MonitorInfo.cbSize := SizeOf(MonitorInfo);
 
-     if Apis.funcGetMonitorInfoW(M, @MonitorInfo) then begin
 
-       if not WorkArea then
 
-         Result := MonitorInfo.rcMonitor
 
-       else
 
-         Result := MonitorInfo.rcWork;
 
-       Exit;
 
-     end;
 
-   end;
 
-   Result := GetRectOfPrimaryMonitor(WorkArea);
 
- end;
 
- function CallTrackMouseEvent(const Wnd: HWND; const Flags: DWORD): Boolean;
 
- var
 
-   Track: TTrackMouseEvent;
 
- begin
 
-   Track.cbSize := SizeOf(Track);
 
-   Track.dwFlags := Flags;
 
-   Track.hwndTrack := Wnd;
 
-   Track.dwHoverTime := 0;
 
-   Result := TrackMouseEvent(Track);
 
- end;
 
- function EnumFontsProc(const lplf: TLogFont; const lptm: TTextMetric;
 
-   dwType: DWORD; lpData: LPARAM): Integer; stdcall;
 
- begin
 
-   Boolean(Pointer(lpData)^) := True;
 
-   Result := 0;
 
- end;
 
- function CreateRotatedFont(DC: HDC): HFONT;
 
- { Creates a font based on the DC's current font, but rotated 270 degrees }
 
- var
 
-   LogFont: TLogFont;
 
-   TM: TTextMetric;
 
-   VerticalFontName: array[0..LF_FACESIZE-1] of Char;
 
-   VerticalFontExists: Boolean;
 
- begin
 
-   if GetObject(GetCurrentObject(DC, OBJ_FONT), SizeOf(LogFont),
 
-      @LogFont) = 0 then begin
 
-     { just in case... }
 
-     Result := 0;
 
-     Exit;
 
-   end;
 
-   LogFont.lfEscapement := 2700;
 
-   LogFont.lfOrientation := 2700;
 
-   LogFont.lfOutPrecision := OUT_TT_ONLY_PRECIS;  { needed for Win9x }
 
-   { Don't let a random TrueType font be substituted when MS Sans Serif or
 
-     Microsoft Sans Serif are used. Hard-code Arial. }
 
-   if (StrIComp(LogFont.lfFaceName, 'MS Sans Serif') = 0) or
 
-      (StrIComp(LogFont.lfFaceName, 'Microsoft Sans Serif') = 0) then begin
 
-     StrPCopy(LogFont.lfFaceName, 'Arial');
 
-     { Set lfHeight to the actual height of the current font. This is needed
 
-       to work around a Windows 98 issue: on a clean install of the OS,
 
-       SPI_GETNONCLIENTMETRICS returns -5 for lfSmCaptionFont.lfHeight. This is
 
-       wrong; it should return -11 for an 8 pt font. With normal, unrotated text
 
-       this actually displays correctly, since MS Sans Serif doesn't support
 
-       sizes below 8 pt. However, when we change to a TrueType font like Arial,
 
-       this becomes a problem because it'll actually create a font that small. }
 
-     if GetTextMetrics(DC, TM) then begin
 
-       { If the original height was negative, keep it negative }
 
-       if LogFont.lfHeight <= 0 then
 
-         LogFont.lfHeight := -(TM.tmHeight - TM.tmInternalLeading)
 
-       else
 
-         LogFont.lfHeight := TM.tmHeight;
 
-     end;
 
-   end;
 
-   { Use a vertical font if available so that Asian characters aren't drawn
 
-     sideways }
 
-   if StrLen(LogFont.lfFaceName) < SizeOf(VerticalFontName)-1 then begin
 
-     VerticalFontName[0] := '@';
 
-     StrCopy(@VerticalFontName[1], LogFont.lfFaceName);
 
-     VerticalFontExists := False;
 
-     EnumFonts(DC, VerticalFontName, @EnumFontsProc, @VerticalFontExists);
 
-     if VerticalFontExists then
 
-       StrCopy(LogFont.lfFaceName, VerticalFontName);
 
-   end;
 
-   Result := CreateFontIndirect(LogFont);
 
- end;
 
- procedure DrawRotatedText(const DC: HDC; AText: String; const ARect: TRect;
 
-   const AFormat: Cardinal);
 
- { Like DrawText, but draws the text at a 270 degree angle.
 
-   The format flag this function respects are
 
-   DT_NOPREFIX, DT_HIDEPREFIX, DT_CENTER, DT_END_ELLIPSIS, DT_NOCLIP }
 
- var
 
-   RotatedFont, SaveFont: HFONT;
 
-   TextMetrics: TTextMetric;
 
-   X, Y, P, I, SU, FU, W: Integer;
 
-   SaveAlign: UINT;
 
-   SavePen, Pen: HPEN;
 
-   Clip: Boolean;
 
-   function GetSize(DC: HDC; const S: string): Integer;
 
-   var
 
-     Size: TSize;
 
-   begin
 
-     GetTextExtentPoint32(DC, PChar(S), Length(S), Size);
 
-     Result := Size.cx;
 
-   end;
 
- begin
 
-   if Length(AText) = 0 then Exit;
 
-   RotatedFont := CreateRotatedFont(DC);
 
-   SaveFont := SelectObject(DC, RotatedFont);
 
-   GetTextMetrics(DC, TextMetrics);
 
-   X := ARect.Left + ((ARect.Right - ARect.Left) - TextMetrics.tmHeight) div 2;
 
-   Clip := (AFormat and DT_NOCLIP) <> DT_NOCLIP;
 
-   { Find the index of the character that should be underlined. Delete '&'
 
-     characters from the string. Like DrawText, only the last prefixed character
 
-     will be underlined. }
 
-   P := 0;
 
-   I := 1;
 
-   if (AFormat and DT_NOPREFIX) <> DT_NOPREFIX then
 
-     while I <= Length(AText) do begin
 
-       if CharInSet(AText[I], LeadBytes) then
 
-         Inc(I)
 
-       else if AText[I] = '&' then begin
 
-         Delete(AText, I, 1);
 
-         { Note: PChar cast is so that if Delete deleted the last character in
 
-           the string, we don't step past the end of the string (which would cause
 
-           an AV if AText is now empty), but rather look at the null character
 
-           and treat it as an accelerator key like DrawText. }
 
-         if PChar(AText)[I-1] <> '&' then
 
-           P := I;
 
-       end;
 
-       Inc(I);
 
-     end;
 
-   if (AFormat and DT_END_ELLIPSIS) = DT_END_ELLIPSIS then
 
-   begin
 
-     if (Length(AText) > 1) and (GetSize(DC, AText) > ARect.Bottom - ARect.Top) then
 
-     begin
 
-       W := ARect.Bottom - ARect.Top;
 
-       if W > 2 then
 
-       begin
 
-         Delete(AText, Length(AText), 1);
 
-         while (Length(AText) > 1) and (GetSize(DC, AText + '...') > W) do
 
-           Delete(AText, Length(AText), 1);
 
-       end
 
-       else AText := AText[1];
 
-       if P > Length(AText) then P := 0;
 
-       AText := AText + '...';
 
-     end;
 
-   end;
 
-   if (AFormat and DT_CENTER) = DT_CENTER then
 
-     Y := ARect.Top + ((ARect.Bottom - ARect.Top) - GetSize(DC, AText)) div 2
 
-   else
 
-     Y := ARect.Top;
 
-   if Clip then
 
-   begin
 
-     SaveDC(DC);
 
-     with ARect do IntersectClipRect(DC, Left, Top, Right, Bottom);
 
-   end;
 
-   SaveAlign := SetTextAlign(DC, TA_BOTTOM);
 
-   TextOut(DC, X, Y, PChar(AText), Length(AText));
 
-   SetTextAlign(DC, SaveAlign);
 
-   { Underline }
 
-   if (P > 0) and (AFormat and DT_HIDEPREFIX = 0) then begin
 
-     SU := GetTextWidth(DC, Copy(AText, 1, P-1), False);
 
-     FU := SU + GetTextWidth(DC, PChar(AText)[P-1], False);
 
-     Inc(X, TextMetrics.tmDescent - 2);
 
-     Pen := CreatePen(PS_SOLID, 1, GetTextColor(DC));
 
-     SavePen := SelectObject(DC, Pen);
 
-     MoveToEx(DC, X, Y + SU, nil);
 
-     LineTo(DC, X, Y + FU);
 
-     SelectObject(DC, SavePen);
 
-     DeleteObject(Pen);
 
-   end;
 
-   if Clip then RestoreDC(DC, -1);
 
-   SelectObject(DC, SaveFont);
 
-   DeleteObject(RotatedFont);
 
- end;
 
- function NeedToPlaySound(const Alias: String): Boolean;
 
- { This function checks the registry to see if the specified sound event alias
 
-   is assigned to a file.
 
-   The purpose of having this function is so it can avoid calls to PlaySound if
 
-   possible, because on Windows 2000 there is an annoying 1/3 second delay on
 
-   the first call to PlaySound.
 
-   Windows Explorer actually uses this same technique when playing sounds for
 
-   the Start menu. }
 
- var
 
-   K: HKEY;
 
-   Data: array[0..3] of WideChar;
 
-   DataSize: DWORD;
 
-   ErrorCode: Longint;
 
- begin
 
-   Result := False;
 
-   if RegOpenKeyEx(HKEY_CURRENT_USER,
 
-      PChar('AppEvents\Schemes\Apps\.Default\' + Alias + '\.Current'),
 
-      0, KEY_QUERY_VALUE, K) = ERROR_SUCCESS then begin
 
-     DataSize := SizeOf(Data);
 
-     { Note: Use the 'W' version of RegQueryValueEx for more speed }
 
-     ErrorCode := RegQueryValueExW(K, nil, nil, nil, @Data, @DataSize);
 
-     if ((ErrorCode = ERROR_SUCCESS) and (Data[0] <> #0)) or
 
-        (ErrorCode = ERROR_MORE_DATA) then
 
-       Result := True;
 
-     RegCloseKey(K);
 
-   end;
 
- end;
 
- function Max(A, B: Integer): Integer;
 
- begin
 
-   if A >= B then
 
-     Result := A
 
-   else
 
-     Result := B;
 
- end;
 
- function Min(A, B: Integer): Integer;
 
- begin
 
-   if A <= B then
 
-     Result := A
 
-   else
 
-     Result := B;
 
- end;
 
- function FindAccelChar(const S: String): Char;
 
- { Finds the last accelerator key in S. Returns #0 if no accelerator key was
 
-   found. '&&' is ignored. }
 
- var
 
-   P: PChar;
 
- begin
 
-   P := PChar(S);
 
-   Result := #0;
 
-   while True do begin
 
-     P := AnsiStrScan(P, '&');
 
-     if P = nil then Break;
 
-     Inc(P);
 
-     if P^ <> '&' then begin
 
-       if P^ = #0 then Break;
 
-       Result := P^;
 
-     end;
 
-     Inc(P);
 
-   end;
 
- end;
 
- function GetInputLocaleCodePage: UINT;
 
- { Returns the code page identifier of the active input locale, or CP_ACP if
 
-   for some unknown reason it couldn't be determined. }
 
- var
 
-   Buf: array[0..15] of Char;
 
-   ErrorCode: Integer;
 
- begin
 
-   if GetLocaleInfo(GetKeyboardLayout(0) and $FFFF, LOCALE_IDEFAULTANSICODEPAGE,
 
-      Buf, SizeOf(Buf)) > 0 then begin
 
-     Buf[High(Buf)] := #0;  { ensure null termination, just in case... }
 
-     Val(Buf, Result, ErrorCode);
 
-     { Just to be *completely* safe, verify that the code page returned by
 
-       GetLocaleInfo actually exists. The result of this function may be fed
 
-       into WideCharToMultiByte, and we don't want WideCharToMultiByte to fail
 
-       entirely because of a bad code page. }
 
-     if (ErrorCode <> 0) or not IsValidCodePage(Result) then
 
-       Result := CP_ACP;
 
-   end
 
-   else
 
-     Result := CP_ACP;
 
- end;
 
- end.
 
 
  |