unit MPCommonObjects;

// Version 1.7.0
//
// 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/
//
// Alternatively, you may redistribute this library, use and/or modify it under the terms of the
// GNU Lesser General Public License as published by the Free Software Foundation;
// either version 2.1 of the License, or (at your option) any later version.
// You may obtain a copy of the LGPL at http://www.gnu.org/copyleft/.
//
// 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 initial developer of this code is Jim Kueneman <jimdk@mindspring.com>
// Special thanks to the following in no particular order for their help/support/code
//    Danijel Malik, Robert Lee, Werner Lehmann, Alexey Torgashin, Milan Vandrovec
//
//----------------------------------------------------------------------------

interface

{$I Compilers.inc}
{$I Options.inc}
{$I ..\Include\Addins.inc}

uses
  Windows,
  Messages,
  Classes,
  Controls,
  Graphics,
  SysUtils,
  ActiveX,
  {$IFDEF COMPILER_6_UP}
  RTLConsts,
  {$ELSE}
  Consts,
  {$ENDIF}
  {$IFDEF COMPILER_7_UP}
  Themes,
  UxTheme,
  {$ELSE}
    {$IFDEF USETHEMES}
    TmSchema,
    UxTheme,
    {$ENDIF}
  {$ENDIF}
  {$IFDEF TNTSUPPORT}
  TntClasses,
  TntWideStrings,
  {$ENDIF}
  ShlObj,
  ShellAPI,
  ImgList,
  TypInfo,
  MPShellTypes,
  MPResources;

const
  IID_ICommonExtractObj = '{7F667930-E47B-4474-BA62-B100D7DBDA70}';

type
  TILIsParent = function(PIDL1: PItemIDList; PIDL2: PItemIDList;
    ImmediateParent: LongBool): LongBool; stdcall;
  TILIsEqual = function(PIDL1: PItemIDList; PIDL2: PItemIDList): LongBool; stdcall;

  TCommonImageIndexInteger = type Integer;


  TStringListEx = class(TStringList)
  private
    {$IFNDEF COMPILER_7_UP}
    FNameValueSeparator: Char;

    function GetNameValueSeparator: Char;
    function GetValueFromIndex(Index: Integer): string;
    procedure SetNameValueSeparator(const Value: Char);
    procedure SetValueFromIndex(Index: Integer; const Value: string);
    {$ENDIF}
  public
    {$IFNDEF COMPILER_7_UP}
    property ValueFromIndex[Index: Integer]: string read GetValueFromIndex write SetValueFromIndex;
    property NameValueSeparator: Char read GetNameValueSeparator write SetNameValueSeparator;
    {$ENDIF}
  end;


type
  TCommonPIDLManager = class;  // forward
  TCommonPIDLList = class;     // forward

  TPIDLArray = array of PItemIDList;
  TRelativePIDLArray = TPIDLArray;
  TAbsolutePIDLArray = TPIDLArray;

  ICommonExtractObj = interface
  [IID_ICommonExtractObj]
    function GetObj: TObject;
    property Obj: TObject read GetObj;
  end;

  // In the ShellContextMenu items may be removed by not supplying the menu with
  // these items.  Note that by including them is DOES not mean that the items will
  // in the menu.  If the items do not support the action the shell with automatically
  // remove the items.
  TCommonShellContextMenuAction = (
    cmaCopy,
    cmaCut,
    cmaPaste,
    cmaDelete,
    cmaRename,
    cmaProperties,
    cmaShortCut
  );
  TCommonShellContextMenuActions = set of TCommonShellContextMenuAction;

  TCommonShellContextMenuExtension = (
    cmeAllFilesystemObjects, // Add the Menu Extensions registered under HKEY_CLASSES_ROOT\AllFilesystemObjects such as Send To item
    cmeDirectory,      // Add the Menu Extensions registered under HKEY_CLASSES_ROOT\Directory
    cmeDirBackground,  // Add the Menu Extensions registered under HKEY_CLASSES_ROOT\Directory\Background
    cmeFolder,         // Add the Menu Extensions registered under HKEY_CLASSES_ROOT\Folder
    cmeAsterik,        // Add the Menu Extensions registered under HKEY_CLASSES_ROOT\*
    cmeShellDefault,   // Adds special actions like, Explore, Open, Search...,  it depends on what other cme_ types are set, such as the Open/Explore Items
    cmeFileSystemAssociations, // Add the Menu Extensions registered under HKEY_CLASSES_ROOT\FileSystemAssociations\{.ext}
    cmePerceivedType   // Checks for a PerceivedType string in the extension key {.ext} that points to a key in the HKEY_CLASSES_ROOT\FileSystemAssociations\SomePerceivedType such as "image".  Will add the "Print" item
  );
  TCommonShellContextMenuExtensions = set of TCommonShellContextMenuExtension;

  //
  // Encapsulates Theme handles for various objects
  //
  {$IFDEF USETHEMES}
  TCommonThemeManager = class
  private
    FButtonTheme: HTHEME;    // Some useful Themes
    FComboBoxTheme: HTHEME;
    FEditTheme: HTHEME;
    FExplorerBarTheme: HTHEME;
    FHeaderTheme: HTHEME;
    FListviewTheme: HTHEME;
    FLoaded: Boolean;
    FOwner: TWinControl;
    FProgressTheme: HTHEME;
    FRebarTheme: HTHEME;
    FScrollbarTheme: HTheme;
    FTaskBandTheme: HTHEME;
    FTaskBarTheme: HTHEME;
    FTreeviewTheme: HTHEME;
    FWindowTheme: HTHEME;
  public
    constructor Create(AnOwner: TWinControl);
    destructor Destroy; override;

    procedure ThemesFree; dynamic;
    procedure ThemesLoad; dynamic;

    property ButtonTheme: HTHEME read FButtonTheme write FButtonTheme;
    property ComboBoxTheme: HTHEME read FComboBoxTheme write FComboBoxTheme;
    property EditThemeTheme: HTHEME read FEditTheme write FEditTheme;
    property ExplorerBarTheme: HTHEME read FExplorerBarTheme write FExplorerBarTheme;
    property HeaderTheme: HTHEME read FHeaderTheme write FHeaderTheme;
    property ListviewTheme: HTHEME read FListviewTheme write FListviewTheme;
    property Loaded: Boolean read FLoaded;
    property Owner: TWinControl read FOwner;
    property ProgressTheme: HTHEME read FProgressTheme write FProgressTheme;
    property RebarTheme: HTHEME read FRebarTheme write FRebarTheme;
    property ScrollbarTheme: HTheme read FScrollbarTheme write FScrollbarTheme;
    property TaskBandTheme: HTHEME read FTaskBandTheme write FTaskBandTheme;
    property TaskBarTheme: HTHEME read FTaskBarTheme write FTaskBarTheme;
    property TreeviewTheme: HTHEME read FTreeviewTheme write FTreeviewTheme;
    property WindowTheme: HTHEME read FWindowTheme write FWindowTheme;
  end;
  {$ENDIF USETHEMES}

  //
  // TWinControl that has a canvas and a few methods/properites for
  // locking the canvas for higher performance drawing.  Also handles
  // XP and above theme support
  //
  TCommonCanvasControl = class(TCustomControl)
  private
    FCanvas: TControlCanvas;
    FImagesExtraLarge: TImageList;
    FImagesLarge: TImageList;
    FImagesSmall: TImageList;
    FOnEndUpdate: TNotifyEvent;
    FThemed: Boolean;
    {$IFDEF USETHEMES}FThemes: TCommonThemeManager;{$ENDIF USETHEMES}
    function GetCanvas: TControlCanvas;
    function GetThemed: Boolean;
    procedure SetThemed(const Value: Boolean);
  protected
    FUpdateCount: Integer;
    procedure CreateWnd; override;
    procedure DoEndUpdate;
    procedure DoUpdate; virtual;
    function DrawWithThemes: Boolean;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    procedure WMDestroy(var Msg: TMessage); message WM_DESTROY;
    {$IFDEF USETHEMES}procedure WMThemeChanged(var Message: TMessage); message WM_THEMECHANGED;{$ENDIF USETHEMES}
    property OnEndUpdate: TNotifyEvent read FOnEndUpdate write FOnEndUpdate;
    property Themed: Boolean read GetThemed write SetThemed default True;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    procedure BeginUpdate; virtual;
    procedure EndUpdate(Invalidate: Boolean = True); virtual;
    procedure SafeInvalidateRect(ARect: PRect; ImmediateUpdate: Boolean);

    property Canvas: TControlCanvas read GetCanvas write FCanvas;
    property Color;
    property DragCursor;
    property DragMode;
    {$IFDEF USETHEMES}property Themes: TCommonThemeManager read FThemes;{$ENDIF USETHEMES}
    property UpdateCount: Integer read FUpdateCount;
  end;

  //
  //  Stores the state of a TCanvas so it may be restored later
  //
  TCommonDefaultCanvasState = class
  private
    FBkMode: Longword;
    FFont: TFont;
    FBrush: TBrush;
    FPen: TPen;
    FCanvasStored: Boolean;
    FCopyMode: TCopyMode;
    FPenPos: TPoint;
    FTextFlags: Integer;
    function GetBrush: TBrush;
    function GetFont: TFont;
    function GetPen: TPen;
  public
    destructor Destroy; override;

    procedure StoreCanvas(ACanvas: TCanvas);
    procedure RestoreCanvas(ACanvas: TCanvas);

    property BkMode: Longword read FBkMode;
    property CanvasStored: Boolean read FCanvasStored;
    property CopyMode: TCopyMode read FCopyMode;
    property Font: TFont read GetFont;
    property Brush: TBrush read GetBrush;
    property Pen: TPen read GetPen;
    property PenPos: TPoint read FPenPos;
    property TextFlags: Integer read FTextFlags;
  end;

  //
  //  A specialized TList that contains PItemID's
  //
  PCommonPIDLList = ^TCommonPIDLList;
  TCommonPIDLList = class(TList)
  private
    FLocalPIDLMgr: TCommonPIDLManager;  // this can be in an IDataObject that the shell holds on to, causing our global PIDLMgr to be freed on application destroy before the shell releases the IDataObject
    FOwnsPIDLMgr: Boolean;
    FSharePIDLs: Boolean;    // If true the class will not free the PIDL's automaticlly when destroyed
    FDestroying: Boolean;  // Instance of a PIDLManager used to easily deal with the PIDL's
    function GetLocalPIDLMgr: TCommonPIDLManager;
    function GetPIDL(Index: integer): PItemIDList;
    procedure SetLocalPIDLMgr(const Value: TCommonPIDLManager);
  protected
    property Destroying: Boolean read FDestroying;
    property OwnsPIDLMgr: Boolean read FOwnsPIDLMgr write FOwnsPIDLMgr;
  public
    constructor Create;
    destructor Destroy; override;

    procedure Clear; override;
    procedure CloneList(PIDLList: TCommonPIDLList);
    function CopyAdd(PIDL: PItemIDList): Integer;
    function FindPIDL(TestPIDL: PItemIDList): Integer;
    function LoadFromStream( Stream: TStream): Boolean; virtual;
    function SaveToStream( Stream: TStream): Boolean; virtual;

    property LocalPIDLMgr: TCommonPIDLManager read GetLocalPIDLMgr write SetLocalPIDLMgr;
    property SharePIDLs: Boolean read FSharePIDLs write FSharePIDLs;
  end;

  //
  // TCoolPIDLManager is a class the encapsulates PIDLs and makes them easier to
  // handle.
  //
  TCommonPIDLManager = class
  private
  protected
    FMalloc: IMalloc;  // The global Memory allocator
  public
    constructor Create;
    destructor Destroy; override;

    function AllocStrGlobal(SourceStr: WideString): POleStr;
    function AppendPIDL(DestPIDL, SrcPIDL: PItemIDList): PItemIDList;
    function BindToParent(AbsolutePIDL: PItemIDList; var Folder: IShellFolder): Boolean;
    function CopyPIDL(APIDL: PItemIDList): PItemIDList;
    function EqualPIDL(PIDL1, PIDL2: PItemIDList): Boolean;
    procedure FreeAndNilPIDL(var PIDL: PItemIDList);
    procedure FreeOLEStr(OLEStr: LPWSTR);
    procedure FreePIDL(PIDL: PItemIDList);
    function CopyLastID(IDList: PItemIDList): PItemIDList;
    function GetPointerToLastID(IDList: PItemIDList): PItemIDList;
    function IDCount(APIDL: PItemIDList): integer;
    function IsDesktopFolder(APIDL: PItemIDList): Boolean;
    function IsSubPIDL(FullPIDL, SubPIDL: PItemIDList): Boolean;
    function NextID(APIDL: PItemIDList): PItemIDList;
    function PIDLSize(APIDL: PItemIDList): integer;
    function LoadFromStream(Stream: TStream): PItemIDList;
    procedure ParsePIDL(AbsolutePIDL: PItemIDList; var PIDLList: TCommonPIDLList; AllAbsolutePIDLs: Boolean);
    function StringToPIDL(PIDLStr: string): PItemIDList;
    function StripLastID(IDList: PItemIDList): PItemIDList; overload;
    function StripLastID(IDList: PItemIDList; var Last_CB: Word; var LastID: PItemIDList): PItemIDList; overload;
    procedure SaveToStream(Stream: TStream; PIDL: PItemIdList);

    property Malloc: IMalloc read FMalloc;
  end;


  //
  // Helper object to write basic property types to a Stream
  //
  TCommonMemoryStreamHelper = class
  public
    function ReadBoolean(S: TStream): Boolean;
    function ReadColor(S: TStream): TColor;
    function ReadInt64(S: TStream): Int64;
    function ReadInteger(S: TStream): Integer;
    function ReadString(S: TStream): string;
    function ReadWideString(S: TStream): WideString;
    function ReadExtended(S: TStream): Extended;
    procedure ReadStream(SourceStream, TargetStream: TStream);
 //   procedure ReadPublishedProperties(S: TStream; Instance: TObject; RecurseSubClasses: Boolean);
    procedure WriteBoolean(S: TStream; Value: Boolean);
    procedure WriteColor(S: TStream; Value: TColor);
    procedure WriteExtended(S: TStream; Value: Extended);
    procedure WriteInt64(S: TStream; Value: Int64);
    procedure WriteInteger(S: TStream; Value: Integer);
    procedure WriteStream(SourceStream, TargetStream: TStream);
//    procedure WritePublishedProperties(S: TStream; Instance: TObject; RecurseSubClasses: Boolean);
    procedure WriteString(S: TStream; Value: string);
    procedure WriteWideString(S: TStream; Value: WideString);
  end;

  //
  // MemoryStream that knows how to write/read basic data types
  //
  TCommonStream = class(TMemoryStream)
  public
    function ReadBoolean: Boolean;
    function ReadByte: Byte;
    function ReadInteger: Integer;
    function ReadString: string;
    function ReadStringList: TStringList;
    function ReadWideString: WideString;

    procedure WriteBoolean(Value: Boolean);
    procedure WriteByte(Value: Byte);
    procedure WriteInteger(Value: Integer);
    procedure WriteString(const Value: string);
    procedure WriteStringList(Value: TStringList);
    procedure WriteWideString(const Value: WideString);
  end;

  //
  // The dimension of the Marlett Checkbox Font
  //
  TCommonCheckBound = class
  private
    FBounds: TRect;
    FSize: Integer;
  public
    property Size: Integer read FSize write FSize;
    property Bounds: TRect read FBounds write FBounds;
  end;

  //
  // The Stores the dimensions for various sizes of the Marlett Checkbox Font
  //
  TCommonCheckBoundManager = class
  private
    FList: TList;
    function GetBound(Size: Integer): TRect;
    function GetCheckBound(Index: Integer): TCommonCheckBound;
  protected
    procedure Clear;
    function Find(Size: Integer): TCommonCheckBound;

    property List: TList read FList write FList;
    property CheckBound[Index: Integer]: TCommonCheckBound read GetCheckBound;
  public
    constructor Create;
    destructor Destroy; override;

    property Bound[Size: Integer]: TRect read GetBound;
  end;

  //
  // Encapsulates the System image lists
  //
  TSysImageListSize =  (
    sisSmall,    // Large System Images
    sisLarge,    // Small System Images
    sisExtraLarge  // Extra Large Images (48x48)
  );

  TCommonSysImages = class(TImageList)
  private
    FImageSize: TSysImageListSize;
    FJumboImages: IImageList;
    procedure SetImageSize(const Value: TSysImageListSize);
  protected
    procedure RecreateHandle;
    procedure Flush;
    property JumboImages: IImageList read FJumboImages;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    property ImageSize: TSysImageListSize read FImageSize write SetImageSize;
  end;

  function ExtraLargeSysImages: TCommonSysImages;
  function LargeSysImages: TCommonSysImages;
  function SmallSysImages: TCommonSysImages;

  procedure FlushImageLists;
procedure CreateFullyQualifiedShellDataObject(AbsolutePIDLs: TAbsolutePIDLArray; var ADataObject: IDataObject);
var
  StreamHelper: TCommonMemoryStreamHelper;
  ILIsParent: TILIsParent = nil;
  ILIsEqual: TILIsEqual = nil;
  Checks: TCommonCheckBoundManager;
  MarlettFont: TFont;
  

implementation

uses
  MPCommonUtilities,
  MPDataObject;

var
  FreeShellLib: Boolean = False;
  ShellDLL: HMODULE = 0;
  FExtraLargeSysImages: TCommonSysImages = nil;
  FLargeSysImages: TCommonSysImages = nil;
  FSmallSysImages: TCommonSysImages = nil;
  PIDLMgr: TCommonPIDLManager = nil;

{$IFNDEF COMPILER_6_UP}
function GUIDToString(const GUID: TGUID): string;
var
  P: PWideChar;
begin
  Result := '';
  if Succeeded(StringFromCLSID(GUID, P)) then
  begin
    Result := P;
    CoTaskMemFree(P);
  end
end;
{$ENDIF}

procedure FlushImageLists;
begin
  if Assigned(FSmallSysImages) then
    FSmallSysImages.Flush;
  if Assigned(FLargeSysImages) then
    FLargeSysImages.Flush;
  if Assigned(FExtraLargeSysImages) then
    FExtraLargeSysImages.Flush
end;

function ExtraLargeSysImages: TCommonSysImages;
begin
  if not Assigned(FExtraLargeSysImages) then
  begin
    FExtraLargeSysImages := TCommonSysImages.Create(nil);
    FExtraLargeSysImages.ImageSize := sisExtraLarge;
  end;
  Result := FExtraLargeSysImages
end;

function LargeSysImages: TCommonSysImages;
begin
  if not Assigned(FLargeSysImages) then
  begin
    FLargeSysImages := TCommonSysImages.Create(nil);
    FLargeSysImages.ImageSize := sisLarge;
  end;
  Result := FLargeSysImages
end;

function SmallSysImages: TCommonSysImages;
begin
  if not Assigned(FSmallSysImages) then
  begin
    FSmallSysImages := TCommonSysImages.Create(nil);
    FSmallSysImages.ImageSize := sisSmall;
  end;
  Result := FSmallSysImages
end;

function SHGetImageList(iImageList: Integer; const RefID: TGUID; out ppvOut): HRESULT;
// Retrieves the system ImageList interface
var
  ImageList: TSHGetImageList;
begin
  Result := E_NOTIMPL;
  if (Win32Platform = VER_PLATFORM_WIN32_NT) then
  begin
    ShellDLL := LoadLibrary(Shell32);
    if ShellDLL <> 0 then
    begin
      ImageList := GetProcAddress(ShellDLL, PChar(727));
      if (Assigned(ImageList)) then
        Result := ImageList(iImageList, RefID, ppvOut);
    end
  end;
end;

procedure CreateFullyQualifiedShellDataObject(AbsolutePIDLs: TAbsolutePIDLArray; var ADataObject: IDataObject);
var
  ShellIDList: TCommonShellIDList;
  APIDLList: TCommonPIDLList;
  i: Integer;
  HDrop: TCommonHDrop;
  DragLoop: TCommonInShellDragLoop;
  {$IFDEF TNTSUPPORT}
  FileListW: TTntStringList;
  {$ENDIF}
  FileListA: TStringList;
  DesktopPIDL, LastID: PItemIDList;
  DesktopFolder, Folder: IShellFolder;
  Flags: UINT;
  StrRet: TStrRet;
begin
  ADataObject := TCommonDataObject.Create;
  if Assigned(AbsolutePIDLs) then
  begin
    APIDLList := TCommonPIDLList.Create;
    APIDLList.SharePIDLs := True;
    ShellIDList := TCommonShellIDList.Create;
    DragLoop := TCommonInShellDragLoop.Create;
    HDrop := TCommonHDrop.Create;
    {$IFDEF TNTSUPPORT}
    FileListW := TTntStringList.Create;
    {$ENDIF}
    FileListA := TStringList.Create;
    try
      SHGetSpecialFolderLocation(0, CSIDL_DESKTOP, DesktopPIDL);
      SHGetDesktopFolder(DesktopFolder);
      APIDLList.Add(DesktopPIDL);
      // Add all the PIDL's from all the DataObjects based off the desktop (Absolute PIDLs)
      for i := 0 to Length(AbsolutePIDLs) - 1 do
      begin
        APIDLList.Add(AbsolutePIDLs[i]);

        if PIDLMgr.BindToParent(AbsolutePIDLs[i], Folder) then
        begin
          LastID := PIDLMgr.GetPointerToLastID(AbsolutePIDLs[i]);
          Flags := SFGAO_FILESYSTEM;
          if Succeeded(Folder.GetAttributesOf(1, LastID, Flags)) then
            if SFGAO_FILESYSTEM and Flags <> 0 then
            begin
              FillChar(StrRet, SizeOf(StrRet), #0);
              Flags := SHGDN_FORPARSING;
              if Succeeded(Folder.GetDisplayNameOf(LastID, Flags, StrRet)) then
              begin
                {$IFDEF TNTSUPPORT}
                if IsUnicode then
                  FileListW.Add(StrRetToStr(StrRet, LastID))
                else
                  FileListA.Add(StrRetToStr(StrRet, LastID));
                {$ELSE}
                FileListA.Add(StrRetToStr(StrRet, LastID));
                {$ENDIF}
              end;
            end
        end
      end;
      ShellIDList.AssignPIDLs(APIDLList);
      {$IFDEF TNTSUPPORT}
      if IsUnicode then
        HDrop.AssignFilesW(FileListW)
      else
        HDrop.AssignFilesA(FileListA);
      {$ELSE}
      HDrop.AssignFilesA(FileListA);
      {$ENDIF}
      ShellIDList.SaveToDataObject(ADataObject);
      HDrop.SaveToDataObject(ADataObject);
      DragLoop.SaveToDataObject(ADataObject)
    finally
      ShellIDList.Free;
      HDrop.Free;
      {$IFDEF TNTSUPPORT}
      FileListW.Free;
      {$ENDIF}
      FileListA.Free;
      DragLoop.Free;
      APIDLList.Free;
      PIDLMgr.FreePIDL(DesktopPIDL)
    end
  end
end;

{ TCoolDefaultCanvasState }

destructor TCommonDefaultCanvasState.Destroy;
begin
  inherited;
  FreeAndNil(FBrush);
  FreeAndNil(FFont);
  FreeAndNil(FPen);
end;

function TCommonDefaultCanvasState.GetBrush: TBrush;
begin
  if not Assigned(FBrush) then
    FBrush := TBrush.Create;
  Result := FBrush
end;

function TCommonDefaultCanvasState.GetFont: TFont;
begin
  if not Assigned(FFont) then
    FFont := TFont.Create;
  Result := FFont
end;

function TCommonDefaultCanvasState.GetPen: TPen;
begin
  if not Assigned(FPen) then
    FPen := TPen.Create;
  Result := FPen
end;

procedure TCommonDefaultCanvasState.RestoreCanvas(ACanvas: TCanvas);
begin
  Assert(CanvasStored, 'Trying to restore a canvas that has not been saved');
  SetBkMode(ACanvas.Handle, FBkMode);
  ACanvas.CopyMode := FCopyMode;
  ACanvas.Font.Assign(Font);
  ACanvas.Brush.Assign(Brush);
  ACanvas.Pen.Assign(Pen);
  ACanvas.PenPos := FPenPos;
  ACanvas.TextFlags := FTextFlags;
  SelectClipRgn(ACanvas.Handle, 0);
end;

procedure TCommonDefaultCanvasState.StoreCanvas(ACanvas: TCanvas);
begin
  FCanvasStored := True;
  FBkMode := GetBkMode(ACanvas.Handle);
  FCopyMode := ACanvas.CopyMode;
  Font.Assign(ACanvas.Font);
  Brush.Assign(ACanvas.Brush);
  Pen.Assign(ACanvas.Pen);
  FPenPos := ACanvas.PenPos;
  FTextFlags := ACanvas.TextFlags;
end;

{ TCommonCanvasControl }

function TCommonCanvasControl.DrawWithThemes: Boolean;
begin
  {$IFDEF USETHEMES}
  Result := Themed and Themes.Loaded;
  {$ELSE}
  Result := False;
  {$ENDIF USETHEMES}
end;

function TCommonCanvasControl.GetThemed: Boolean;
begin
  Result := False;
  {$IFDEF USETHEMES}
  if not (csLoading in ComponentState) then
    Result := FThemed and UseThemes;
  {$ENDIF USETHEMES}
end;

procedure TCommonCanvasControl.BeginUpdate;
//
// If ReIndex = False it is up to the user to understand when it is necessary
// to ReIndex different objects.  By doing so performance may be enhanced
// drasticlly on large data sets.
begin
  Inc(FUpdateCount);
end;

constructor TCommonCanvasControl.Create(AOwner: TComponent);
begin
  inherited;
  FCanvas := TControlCanvas.Create;
  Canvas.Control := Self;
  // No notifications for font change
  Font.OnChange := nil;
  {$IFDEF USETHEMES}FThemes := TCommonThemeManager.Create(Self);{$ENDIF USETHEMES}
  FThemed := True;
end;

destructor TCommonCanvasControl.Destroy;
begin
  inherited;
  {$IFDEF USETHEMES}FreeAndNil(FThemes);{$ENDIF USETHEMES}
  FreeAndNil(FCanvas);
end;

procedure TCommonCanvasControl.CreateWnd;
begin
  inherited CreateWnd;
  {$IFDEF USETHEMES}Themes.ThemesLoad;{$ENDIF USETHEMES}
end;

procedure TCommonCanvasControl.DoEndUpdate;
begin
  if Assigned(OnEndUpdate) then
    OnEndUpdate(Self)
end;

procedure TCommonCanvasControl.DoUpdate;
begin
end;

procedure TCommonCanvasControl.EndUpdate(Invalidate: Boolean = True);
begin
  Dec(FUpdateCount);
  if (UpdateCount <= 0) then
  begin
    FUpdateCount := 0;
    DoUpdate;
    if Invalidate and HandleAllocated then
      UpdateWindow(Handle);
    DoEndUpdate;
  end
end;

function TCommonCanvasControl.GetCanvas: TControlCanvas;
begin
  FCanvas.Font.Assign(Font);
  FCanvas.Brush.Assign(Brush);
  Result := FCanvas;
end;

procedure TCommonCanvasControl.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited;
  if Operation = opRemove then
  begin
    if AComponent = FImagesExtraLarge then
      FImagesExtraLarge := nil
    else
    if AComponent = FImagesLarge then
      FImagesLarge := nil
    else
    if AComponent = FImagesSmall then
      FImagesSmall := nil
  end
end;

procedure TCommonCanvasControl.SafeInvalidateRect(ARect: PRect; ImmediateUpdate: Boolean);
begin
  if HandleAllocated then
  begin
    InvalidateRect(Handle, ARect, False);
    if ImmediateUpdate then
      UpdateWindow(Handle)
  end
end;

procedure TCommonCanvasControl.SetThemed(const Value: Boolean);
begin
  if Value <> FThemed then
  begin
    FThemed := Value;
    {$IFDEF USETHEMES}
    if Value then    
      Themes.ThemesLoad
    else
      Themes.ThemesFree;
    if HandleAllocated then
    begin
      // This is the only way I could get the window to redraw the NonClient areas
      // RedrawWindow did not work either.
      Visible := not Visible;
      Visible := not Visible;
      SafeInvalidateRect(nil, True);
    end;
    {$ENDIF USETHEMES}
  end
end;

procedure TCommonCanvasControl.WMDestroy(var Msg: TMessage);
begin
  {$IFDEF USETHEMES}Themes.ThemesFree;{$ENDIF USETHEMES}
  inherited;
end;

{$IFDEF USETHEMES}
procedure TCommonCanvasControl.WMThemeChanged(var Message: TMessage);
begin
  inherited;
  Themes.ThemesFree;
  Themes.ThemesLoad;
end;
{$ENDIF USETHEMES}

{ TCoolPIDLList }

constructor TCommonPIDLList.Create;
begin
  inherited Create;
end;

function TCommonPIDLList.GetLocalPIDLMgr: TCommonPIDLManager;
begin
  if not Assigned(FLocalPIDLMgr) then
  begin
    FLocalPIDLMgr := TCommonPIDLManager.Create;
    OwnsPIDLMgr := True;
  end;
  Result := FLocalPIDLMgr
end;

procedure TCommonPIDLList.Clear;
var
  i: integer;
begin
  if {(not Destroying) or} Assigned(PIDLMgr) then
  begin
    if not SharePIDLs and Assigned(PIDLMgr)then
      for i := 0 to Count - 1 do
        LocalPIDLMgr.FreePIDL( PItemIDList( Items[i]));
  end;
  inherited;
end;

procedure TCommonPIDLList.CloneList(PIDLList: TCommonPIDLList);
var
  i: Integer;
begin
  if Assigned(PIDLList) then
    for i := 0 to Count - 1 do
      PIDLList.CopyAdd(Items[i])
end;

function TCommonPIDLList.CopyAdd(PIDL: PItemIDList): integer;
// Adds a Copy of the passed PIDL to the list
begin
  Result := Add( LocalPIDLMgr.CopyPIDL(PIDL));
end;

destructor TCommonPIDLList.Destroy;
begin
  FDestroying := True;
  if OwnsPIDLMgr then
    FreeAndNil(FLocalPIDLMgr);
  inherited;
end;

function TCommonPIDLList.FindPIDL(TestPIDL: PItemIDList): Integer;
// Finds the index of the PIDL that is equivalent to the passed PIDL.  This is not
// the same as an byte for byte equivalent comparison
var
  i: Integer;
begin
  i := 0;
  Result := -1;
  while (i < Count) and (Result < 0) do
  begin
    if LocalPIDLMgr.EqualPIDL(TestPIDL, GetPIDL(i)) then
      Result := i;
    Inc(i);
  end;
end;

function TCommonPIDLList.GetPIDL(Index: integer): PItemIDList;
begin
  Result := PItemIDList( Items[Index]);
end;

function TCommonPIDLList.LoadFromStream(Stream: TStream): Boolean;
// Loads the PIDL list from a stream
var
  PIDLCount, i: integer;
begin
  Result := True;
  try
    Stream.ReadBuffer(PIDLCount, SizeOf(Integer));
    for i := 0 to PIDLCount - 1 do
      Add( LocalPIDLMgr.LoadFromStream(Stream));
  except
    Result := False;
  end;
end;

function TCommonPIDLList.SaveToStream(Stream: TStream): Boolean;
// Saves the PIDL list to a stream
var
  i: integer;
begin
  Result := True;
  try
    Stream.WriteBuffer(Count, SizeOf(Count));
    for i := 0 to Count - 1 do
      LocalPIDLMgr.SaveToStream(Stream, Items[i]);
  except
    Result := False;
  end;
end;

{ TCommonPIDLManager }

procedure TCommonPIDLList.SetLocalPIDLMgr(const Value: TCommonPIDLManager);
begin
  if Value <> FLocalPIDLMgr then
  begin
    if not OwnsPIDLMgr then
      FreeAndNil(FLocalPIDLMgr);
    OwnsPIDLMgr := False;
    FLocalPIDLMgr := Value;
  end
end;

// Routines to do most anything you would want to do with a PIDL

function TCommonPIDLManager.AppendPIDL(DestPIDL, SrcPIDL: PItemIDList): PItemIDList;
// Returns the concatination of the two PIDLs. Neither passed PIDLs are
// freed so it is up to the caller to free them.
var
  DestPIDLSize, SrcPIDLSize: integer;
begin
  DestPIDLSize := 0;
  SrcPIDLSize := 0;
  // Appending a PIDL to the DesktopPIDL is invalid so don't allow it.
  if Assigned(DestPIDL) then
    if not IsDesktopFolder(DestPIDL) then
      DestPIDLSize := PIDLSize(DestPIDL) - SizeOf(DestPIDL^.mkid.cb);

  if Assigned(SrcPIDL) then
    SrcPIDLSize := PIDLSize(SrcPIDL);

  Result := FMalloc.Alloc(DestPIDLSize + SrcPIDLSize);
  if Assigned(Result) then
  begin
    if Assigned(DestPIDL) and (DestPIDLSize > 0) then
      CopyMemory(Result, DestPIDL, DestPIDLSize);
    if Assigned(SrcPIDL) and (SrcPIDLSize > 0) then
      CopyMemory(Pchar(Result) + DestPIDLSize, SrcPIDL, SrcPIDLSize);
  end;
end;

function TCommonPIDLManager.BindToParent(AbsolutePIDL: PItemIDList; var Folder: IShellFolder): Boolean;
var
  Desktop: IShellFolder;
  Last_CB: Word;
  LastID: PItemIDList;
begin
  SHGetDesktopFolder(Desktop);
  if PIDLMgr.IDCount(AbsolutePIDL) = 1 then
  begin
    Folder := Desktop;
    Result := True
  end else
  begin
    StripLastID(AbsolutePIDL, Last_CB, LastID);
    try
      Result := Succeeded(Desktop.BindToObject(AbsolutePIDL, nil, IShellFolder, Pointer(Folder)))
    finally
      LastID.mkid.cb := Last_CB
    end
  end
end;

function TCommonPIDLManager.CopyPIDL(APIDL: PItemIDList): PItemIDList;
// Copies the PIDL and returns a newly allocated PIDL. It is not associated
// with any instance of TCoolPIDLManager so it may be assigned to any instance.
var
  Size: integer;
begin
  if Assigned(APIDL) then
  begin
    Size := PIDLSize(APIDL);
    Result := FMalloc.Alloc(Size);
    if Result <> nil then
      CopyMemory(Result, APIDL, Size);
  end else
    Result := nil
end;

constructor TCommonPIDLManager.Create;
begin
  inherited Create;
  if SHGetMalloc(FMalloc) = E_FAIL then
    fail
end;

destructor TCommonPIDLManager.Destroy;
begin
  FMalloc := nil;
  inherited
end;

function TCommonPIDLManager.EqualPIDL(PIDL1, PIDL2: PItemIDList): Boolean;
begin
  if Assigned(PIDL1) and Assigned(PIDL2) then
    Result := Boolean( ILIsEqual(PIDL1, PIDL2))
  else
    Result := False
end;

procedure TCommonPIDLManager.FreeOLEStr(OLEStr: LPWSTR);
// Frees an OLE string created by the Shell; as in StrRet
begin
  FMalloc.Free(OLEStr)
end;

procedure TCommonPIDLManager.FreePIDL(PIDL: PItemIDList);
// Frees the PIDL using the shell memory allocator
begin
  if Assigned(PIDL) then
    FMalloc.Free(PIDL)
end;

function TCommonPIDLManager.CopyLastID(IDList: PItemIDList): PItemIDList;
// Returns a copy of the last PID in the list
var
  Count, i: integer;
  PIDIndex: PItemIDList;
begin
  PIDIndex := IDList;
  Count := IDCount(IDList);
  if Count > 1 then
    for i := 0 to Count - 2 do
     PIDIndex := NextID(PIDIndex);
  Result := CopyPIDL(PIDIndex);
end;

function TCommonPIDLManager.GetPointerToLastID(IDList: PItemIDList): PItemIDList;
// Return a pointer to the last PIDL in the complex PIDL passed to it.
// Useful to overlap an Absolute complex PIDL with the single level
// Relative PIDL.
var
  Count, i: integer;
  PIDIndex: PItemIDList;
begin
  if Assigned(IDList) then
  begin
    PIDIndex := IDList;
    Count := IDCount(IDList);
    if Count > 1 then
      for i := 0 to Count - 2 do
       PIDIndex := NextID(PIDIndex);
    Result := PIDIndex;
  end else
    Result := nil
end;

function TCommonPIDLManager.IDCount(APIDL: PItemIDList): integer;
// Counts the number of Simple PIDLs contained in a Complex PIDL.
var
  Next: PItemIDList;
begin
  Result := 0;
  Next := APIDL;
  if Assigned(Next) then
  begin
    while Next^.mkid.cb <> 0 do
    begin
      Inc(Result);
      Next := NextID(Next);
    end
  end
end;

function TCommonPIDLManager.IsDesktopFolder(APIDL: PItemIDList): Boolean;
// Tests the passed PIDL to see if it is the root Desktop Folder
begin
  if Assigned(APIDL) then
    Result := APIDL.mkid.cb = 0
  else
    Result := False
end;

function TCommonPIDLManager.NextID(APIDL: PItemIDList): PItemIDList;
// Returns a pointer to the next Simple PIDL in a Complex PIDL.
begin
  Result := APIDL;
  Inc(PChar(Result), APIDL^.mkid.cb);
end;

function TCommonPIDLManager.PIDLSize(APIDL: PItemIDList): integer;
// Returns the total Memory in bytes the PIDL occupies.
begin
  Result := 0;
  if Assigned(APIDL) then
  begin
    Result := SizeOf( Word);  // add the null terminating last ItemID
    while APIDL.mkid.cb <> 0 do
    begin
      Result := Result + APIDL.mkid.cb;
      APIDL := NextID(APIDL);
    end;
  end;
end;

function TCommonPIDLManager.LoadFromStream(Stream: TStream): PItemIDList;
// Loads the PIDL from a Stream
var
  Size: integer;
begin
  Result := nil;
  if Assigned(Stream) then
  begin
    Stream.ReadBuffer(Size, SizeOf(Integer));
    if Size > 0 then
    begin
      Result := FMalloc.Alloc(Size);
      Stream.ReadBuffer(Result^, Size);
    end
  end
end;

function TCommonPIDLManager.StringToPIDL(PIDLStr: string): PItemIDList;
var
  P: PChar;
begin
  Result := FMalloc.Alloc(Length(PIDLStr));
  P := @PIDLStr[1];
  Move(P^, Result^, Length(PIDLStr));
end;

function TCommonPIDLManager.StripLastID(IDList: PItemIDList): PItemIDList;
// Removes the last PID from the list. Returns the same, shortened, IDList passed
// to the function
var
  MarkerID: PItemIDList;
begin
  Result := IDList;
  MarkerID := IDList;
  if Assigned(IDList) then
  begin
    while IDList.mkid.cb <> 0 do
    begin
      MarkerID := IDList;
      IDList := NextID(IDList);
    end;
    MarkerID.mkid.cb := 0;
  end;
end;

procedure TCommonPIDLManager.SaveToStream(Stream: TStream; PIDL: PItemIdList);
// Saves the PIDL from a Stream
var
  Size: Integer;
begin
  Size := PIDLSize(PIDL);
  Stream.WriteBuffer(Size, SizeOf(Size));
  Stream.WriteBuffer(PIDL^, Size);
end;


function TCommonPIDLManager.StripLastID(IDList: PItemIDList; var Last_CB: Word;
  var LastID: PItemIDList): PItemIDList;
// Strips the last ID but also returns the pointer to where the last CB was and the
// value that was there before setting it to 0 to shorten the PIDL.  All that is necessary
// is to do a LastID^ := Last_CB.mkid.cb to return the PIDL to its previous state.  Used to
// temporarily strip the last ID of a PIDL
var
  MarkerID: PItemIDList;
begin
  Last_CB := 0;
  LastID := nil;
  Result := IDList;
  MarkerID := IDList;
  if Assigned(IDList) then
  begin
    while IDList.mkid.cb <> 0 do
    begin
      MarkerID := IDList;
      IDList := NextID(IDList);
    end;
    Last_CB := MarkerID.mkid.cb;
    LastID := MarkerID;
    MarkerID.mkid.cb := 0;
  end;
end;

function TCommonPIDLManager.IsSubPIDL(FullPIDL, SubPIDL: PItemIDList): Boolean;
// Tests to see if the SubPIDL can be expanded into the passed FullPIDL
var
  i, PIDLLen, SubPIDLLen: integer;
  PIDL: PItemIDList;
  OldCB: Word;
begin
  Result := False;
  if Assigned(FullPIDL) and Assigned(SubPIDL) then
  begin
    SubPIDLLen := IDCount(SubPIDL);
    PIDLLen := IDCount(FullPIDL);
    if SubPIDLLen <= PIDLLen then
    begin
      PIDL := FullPIDL;
      for i := 0 to SubPIDLLen - 1 do
        PIDL := NextID(PIDL);
      OldCB := PIDL.mkid.cb;
      PIDL.mkid.cb := 0;
      try
        Result := ILIsEqual(FullPIDL, SubPIDL);
      finally
        PIDL.mkid.cb := OldCB
      end
    end
  end
end;

procedure TCommonPIDLManager.FreeAndNilPIDL(var PIDL: PItemIDList);
var
  OldPIDL: PItemIDList;
begin
  OldPIDL := PIDL;
  PIDL := nil;
  FreePIDL(OldPIDL)
end;

function TCommonPIDLManager.AllocStrGlobal(SourceStr: WideString): POleStr;
begin
  Result := Malloc.Alloc((Length(SourceStr) + 1) * 2); // Add the null
  if Result <> nil then
    CopyMemory(Result, PWideChar(SourceStr), (Length(SourceStr) + 1) * 2);
end;

procedure TCommonPIDLManager.ParsePIDL(AbsolutePIDL: PItemIDList; var PIDLList: TCommonPIDLList;
  AllAbsolutePIDLs: Boolean);
// Parses the AbsolutePIDL in to its single level PIDLs, if AllAbsolutePIDLs is true
// then each item is not a single level PIDL but an AbsolutePIDL but walking from the
// Desktop up to the passed AbsolutePIDL
var
  OldCB: Word;
  Head, Tail: PItemIDList;
begin
  Head := AbsolutePIDL;
  Tail := Head;
  if Assigned(PIDLList) and Assigned(Head) then
  begin
    while Tail.mkid.cb <> 0 do
    begin
      Tail := NextID(Tail);
      OldCB := Tail.mkid.cb;
      try
        Tail.mkid.cb := 0;
        PIDLList.Add(CopyPIDL(Head));
      finally
        Tail.mkid.cb := OldCB;
      end;
      if not AllAbsolutePIDLs then
        Head := Tail
    end
  end
end;

procedure LoadShell32Functions;
begin
  ShellDLL := GetModuleHandle(PChar(Shell32));
  if ShellDLL = 0 then
  begin
    ShellDLL := LoadLibrary(PChar(Shell32));
    FreeShellLib := True;
  end;
  if ShellDll <> 0 then
  begin
    ILIsEqual := GetProcAddress(ShellDLL, PChar(21));
    ILIsParent := GetProcAddress(ShellDLL, PChar(23));
  end
end;

{ TCommonMemoryStream}
function TCommonMemoryStreamHelper.ReadBoolean(S: TStream): Boolean;
begin
  S.Read(Result, SizeOf(Result));
end;

function TCommonMemoryStreamHelper.ReadColor(S: TStream): TColor;
begin
  S.Read(Result, SizeOf(Result));
end;

function TCommonMemoryStreamHelper.ReadInt64(S: TStream): Int64;
begin
  S.Read(Result, SizeOf(Result));
end;

function TCommonMemoryStreamHelper.ReadInteger(S: TStream): Integer;
begin
  S.Read(Result, SizeOf(Result))
end;

function TCommonMemoryStreamHelper.ReadString(S: TStream): string;
var
  i: Integer;
begin
  i := ReadInteger(S);
  SetLength(Result, i);
  S.Read(PChar(Result)^, i);
end;

function TCommonMemoryStreamHelper.ReadWideString(S: TStream): WideString;
var
  i: Integer;
begin
  i := ReadInteger(S);
  SetLength(Result, i);
  S.Read(PWideChar(Result)^, i * 2);
end;

function TCommonMemoryStreamHelper.ReadExtended(S: TStream): Extended;
begin
  S.Read(Result, SizeOf(Result))
end;

procedure TCommonMemoryStreamHelper.ReadStream(SourceStream, TargetStream: TStream);
var
  Len: Integer;
  X: array of Byte;
begin
  TargetStream.Size := 0;
  SourceStream.Read(Len, SizeOf(Len));
  if Len > 0 then
  begin   
    SetLength(X, Len);
    SourceStream.Read(X[0], Len);
    TargetStream.Write(X[0], Len);
  end
end;

{

Needs to be modified for D5 and D4

procedure TCommonMemoryStreamHelper.ReadPublishedProperties(S: TStream; Instance: TObject; RecurseSubClasses: Boolean);
var
  TypeInfo: PTypeInfo;
  TypeData: PTypeData;
  PropList: PPropList;
  i: Integer;
  Obj: TObject;
begin
  if Assigned(Instance) then
  begin
    TypeInfo := PTypeInfo(Instance.ClassInfo);
    TypeData := GetTypeData(TypeInfo);
    GetMem(PropList, TypeData.PropCount * SizeOf(Pointer));
    try
      GetPropInfos(TypeInfo, PropList);
      for i := 0 to TypeData.PropCount - 1 do
      begin
        case PropList[i].PropType^^.Kind of
          tkClass:
            begin
              if RecurseSubClasses then
              begin
                Obj := GetObjectProp(Instance, PropList[i]);
                ReadPublishedProperties(S, Obj, RecurseSubClasses);
              end
            end;
          tkInteger, tkChar, tkWChar, tkEnumeration, tkSet:
            begin
              SetOrdProp(Instance, PropList[i], ReadInteger(S));
            end;
          tkFloat:
            begin
              SetFloatProp(Instance, PropList[i], ReadExtended(S));
            end;
          tkString:
            begin
              SetStrProp(Instance, PropList[i], ReadString(S));
            end;
          tkWString, tkLString:
            begin
              // It looks like the VCL messes this up completely.
              SetWideStrProp(Instance, PropList[i], ReadWideString(S));
            end;
          tkInt64:
            begin
              SetInt64Prop(Instance, PropList[i], ReadInt64(S));
            end;
        end
      end;
    finally
      FreeMem(PropList);
    end
  end
end;
}
procedure TCommonMemoryStreamHelper.WriteBoolean(S: TStream; Value: Boolean);
begin
  S.Write(Value, SizeOf(Value))
end;

procedure TCommonMemoryStreamHelper.WriteColor(S: TStream; Value: TColor);
begin
  S.Write(Value, SizeOf(Value))
end;

procedure TCommonMemoryStreamHelper.WriteExtended(S: TStream; Value: Extended);
begin
  S.Write(Value, SizeOf(Value))
end;

procedure TCommonMemoryStreamHelper.WriteInt64(S: TStream; Value: Int64);
begin
  S.Write(Value, SizeOf(Value))
end;

procedure TCommonMemoryStreamHelper.WriteInteger(S: TStream; Value: Integer);
begin
  S.Write(Value, SizeOf(Value))
end;

procedure TCommonMemoryStreamHelper.WriteStream(SourceStream,
  TargetStream: TStream);
var
  Len: Integer;
  X: array of Byte;
begin
  Len := SourceStream.Size;
  TargetStream.Write(Len, SizeOf(Len));
  if Len > 0 then
  begin
    SetLength(X, Len);
    SourceStream.Seek(0, 0);
    SourceStream.Read(X[0], Len);
    TargetStream.Write(X[0], Len);
  end
end;

{

Needs to be modified for D5 and D4

procedure TCommonMemoryStreamHelper.WritePublishedProperties(S: TStream; Instance: TObject; RecurseSubClasses: Boolean);
var
  TypeInfo: PTypeInfo;
  TypeData: PTypeData;
  PropList: PPropList;
  i: Integer;
begin
  if Assigned(Instance) then
  begin
    TypeInfo := PTypeInfo(Instance.ClassInfo);
    TypeData := GetTypeData(TypeInfo);
    GetMem(PropList, TypeData.PropCount * SizeOf(Pointer));
    try
      GetPropInfos(TypeInfo, PropList);
      for i := 0 to TypeData.PropCount - 1 do
      begin
        case PropList[i].PropType^^.Kind of
          tkClass:
            begin
              if RecurseSubClasses then
                WritePublishedProperties(S, GetObjectProp(Instance, PropList[i].Name), RecurseSubClasses);
            end;
          tkInteger, tkChar, tkWChar, tkEnumeration, tkSet:
            begin
               WriteInteger(S, GetOrdProp(Instance, PropList[i]))
            end;
          tkFloat:
            begin
              WriteExtended(S, GetFloatProp(Instance, PropList[i]))
            end;
          tkString:
            begin
              WriteString(S, GetStrProp(Instance, PropList[i]))
            end;
          tkWString, tkLString:
            begin
              WriteWideString(S, GetWideStrProp(Instance, PropList[i]));
            end;
          tkInt64:
            begin
              WriteInt64(S, GetInt64Prop(Instance, PropList[i]))
            end;
        end
      end;
    finally
      FreeMem(PropList);
    end
  end
end;
}

procedure TCommonMemoryStreamHelper.WriteString(S: TStream; Value: string);
begin
  WriteInteger(S, Length(Value));
  S.Write(PChar(Value)^, Length(Value))
end;

procedure TCommonMemoryStreamHelper.WriteWideString(S: TStream; Value: WideString);
begin
  WriteInteger(S, Length(Value));
  S.Write(PWideChar(Value)^, Length(Value) * 2)
end;

{$IFDEF USETHEMES}
{ TCommonThemeManager }
constructor TCommonThemeManager.Create(AnOwner: TWinControl);
begin
  inherited Create;
  FOwner := AnOwner;
end;

destructor TCommonThemeManager.Destroy;
begin
  ThemesFree;
  inherited;
end;

procedure TCommonThemeManager.ThemesFree;
begin
  FLoaded := False;
  if FButtonTheme <> 0 then
    CloseThemeData(FButtonTheme);
  FButtonTheme := 0;
  if FListviewTheme <> 0 then
    CloseThemeData(FListviewTheme);
  FListviewTheme := 0;
  if FHeaderTheme <> 0 then
    CloseThemeData(FHeaderTheme);
  FHeaderTheme := 0;
  if FTreeviewTheme <> 0 then
    CloseThemeData(FTreeviewTheme);
  FTreeviewTheme := 0;
  if FExplorerBarTheme <> 0 then
    CloseThemeData(FExplorerBarTheme);
  FExplorerBarTheme := 0;
  if FComboBoxTheme <> 0 then
    CloseThemeData(FComboBoxTheme);
  FComboBoxTheme := 0;
  if FEditTheme <> 0 then
    CloseThemeData(FEditTheme);
  FEditTheme := 0;
  if FRebarTheme <> 0 then
    CloseThemeData(FRebarTheme);
  FRebarTheme := 0;
  if FWindowTheme <> 0 then
    CloseThemeData(FWindowTheme);
  FWindowTheme := 0;
  if FTaskBandTheme <> 0 then
    CloseThemeData(FTaskBandTheme);
  FTaskBandTheme := 0;
  if FTaskBarTheme <> 0 then
    CloseThemeData(FTaskBarTheme);
  FTaskBarTheme := 0;
  if FScrollbarTheme <> 0 then
    CloseThemeData(FScrollbarTheme);
  FScrollbarTheme := 0;
  if FProgressTheme <> 0 then
    CloseThemeData(FProgressTheme);
  FProgressTheme := 0;
end;

procedure TCommonThemeManager.ThemesLoad;
begin
  InitThemeLibrary;
  if Owner.HandleAllocated then
  begin
    if UseThemes then
    begin
      ThemesFree;
      FButtonTheme := OpenThemeData(Owner.Handle, 'button');
      FListviewTheme := OpenThemeData(Owner.Handle, 'listview');
      FHeaderTheme := OpenThemeData(Owner.Handle, 'header');
      FTreeviewTheme := OpenThemeData(Owner.Handle, 'treeview');
      FExplorerBarTheme := OpenThemeData(Owner.Handle, 'explorerbar');
      FComboBoxTheme := OpenThemeData(Owner.Handle, 'combobox');
      FEditTheme := OpenThemeData(Owner.Handle, 'edit');
      FRebarTheme := OpenThemeData(Owner.Handle, 'rebar');
      FWindowTheme := OpenThemeData(Owner.Handle, 'window');
      FTaskBandTheme := OpenThemeData(Owner.Handle, 'taskband');
      FTaskBarTheme := OpenThemeData(Owner.Handle, 'taskbar');
      FScrollbarTheme := OpenThemeData(Owner.Handle, 'scrollbar');
      FProgressTheme := OpenThemeData(Owner.Handle, 'progress');
      FLoaded := True
    end;
    RedrawWindow(Owner.Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE or RDW_NOERASE or RDW_NOCHILDREN);
  end
end;
{$ENDIF USETHEMES}



{ TCommonStream}

function TCommonStream.ReadBoolean: Boolean;
begin
  ReadBuffer(Result, SizeOf(Boolean))
end;

function TCommonStream.ReadByte: Byte;
begin
  ReadBuffer(Result, SizeOf(Byte))
end;

function TCommonStream.ReadInteger: Integer;
begin
  ReadBuffer(Result, SizeOf(Integer))
end;

function TCommonStream.ReadString: string;
var
  Size: LongWord;
begin
  ReadBuffer(Size, SizeOf(LongWord));
  SetLength(Result, Size);
  ReadBuffer(PChar(Result)^, Size)
end;

function TCommonStream.ReadStringList: TStringList;
var
  i, Count: LongWord;
begin
  Result := TStringList.Create;
  ReadBuffer(Count, SizeOf(LongWord));
  for i := 0 to Count - 1 do
    Result.Add(ReadString)
end;

function TCommonStream.ReadWideString: WideString;
var
  Size: LongWord;
begin
  ReadBuffer(Size, SizeOf(LongWord));
  SetLength(Result, Size);
  ReadBuffer(PWideChar(Result)^, Size * 2)
end;

procedure TCommonStream.WriteBoolean(Value: Boolean);
begin
  WriteBuffer(Value, SizeOf(Boolean))
end;

procedure TCommonStream.WriteByte(Value: Byte);
begin
  WriteBuffer(Value, SizeOf(Byte))
end;

procedure TCommonStream.WriteInteger(Value: Integer);
begin
  WriteBuffer(Value, SizeOf(Integer))
end;

procedure TCommonStream.WriteString(const Value: string);
var
  Size: LongWord;
begin
  Size := Length(Value);
  WriteBuffer(Size, SizeOf(Size));
  WriteBuffer(PChar(Value)^, Size);
end;

procedure TCommonStream.WriteStringList(Value: TStringList);
var
  i, Count: LongWord;
begin
  Count := Value.Count;
  WriteBuffer(Count, SizeOf(Count));
  for i := 0 to Count - 1 do
    WriteString(Value[i])
end;

procedure TCommonStream.WriteWideString(const Value: WideString);
var
  Size: LongWord;
begin
  Size := Length(Value);
  WriteBuffer(Size, SizeOf(Size));
  WriteBuffer(PWideChar(Value)^, Size * 2);
end;

{ CheckBoundManager}
constructor TCommonCheckBoundManager.Create;
begin
  List := TList.Create;
end;

destructor TCommonCheckBoundManager.Destroy;
begin
  Clear;
  FreeAndNil(FList);
end;

function TCommonCheckBoundManager.Find(Size: Integer): TCommonCheckBound;
var
  i: Integer;
  Done: Boolean;
begin
  i := 0;
  Done := False;
  Result := nil;
  while (i < List.Count) and not Done do
  begin
    if CheckBound[i].Size = Size then
    begin
      Done := True;
      Result := CheckBound[i]
    end;
    Inc(i)
  end
end;

function TCommonCheckBoundManager.GetBound(Size: Integer): TRect;
var
  Bounds: TCommonCheckBound;
begin
  Bounds := Find(Size);
  if not Assigned(Bounds) then
  begin
    Bounds := TCommonCheckBound.Create;
    List.Add(Bounds);
    Bounds.Size := Size;
    Bounds.Bounds := CheckBounds(Size);
  end;
  Result := Bounds.Bounds;
end;

function TCommonCheckBoundManager.GetCheckBound(Index: Integer): TCommonCheckBound;
begin
  Result := TCommonCheckBound( List[Index])
end;

procedure TCommonCheckBoundManager.Clear;
var
  i: Integer;
begin
  for i := 0 to List.Count - 1 do
    TObject(List[i]).Free;
  List.Clear;
end;

{ TCommonSysImages }
constructor TCommonSysImages.Create(AOwner: TComponent);
begin
  inherited;
  ShareImages := True;
  ImageSize := sisSmall;
  DrawingStyle := dsTransparent
end;

destructor TCommonSysImages.Destroy;
begin
  inherited;
end;

procedure TCommonSysImages.Flush;
begin
  RecreateHandle
end;

procedure TCommonSysImages.RecreateHandle;
var
  PIDL: PItemIDList;
  Malloc: IMalloc;
  FileInfo: TSHFileInfo;
  Flags: Longword;
begin
  Handle := 0;
  if FImageSize = sisExtraLarge then
  begin
    if Succeeded(SHGetImageList(SHIL_EXTRALARGE, IImageList, FJumboImages)) then
      Handle := THandle(FJumboImages)
    else begin
      Flags := SHGFI_PIDL or SHGFI_SYSICONINDEX or SHGFI_LARGEICON;
      SHGetSpecialFolderLocation(0, CSIDL_DESKTOP, PIDL);
      SHGetMalloc(Malloc);
      Handle := SHGetFileInfo(PChar(PIDL), 0, FileInfo, SizeOf(FileInfo), Flags);
      Malloc.Free(PIDL);
    end
  end else
  begin
    SHGetSpecialFolderLocation(0, CSIDL_DESKTOP, PIDL);
    SHGetMalloc(Malloc);
    if FImageSize = sisSmall then
      Flags := SHGFI_PIDL or SHGFI_SYSICONINDEX or SHGFI_SMALLICON
    else
      Flags := SHGFI_PIDL or SHGFI_SYSICONINDEX or SHGFI_LARGEICON;
    Handle := SHGetFileInfo(PChar(PIDL), 0, FileInfo, SizeOf(FileInfo), Flags);
    Malloc.Free(PIDL);
  end;
end;

procedure TCommonSysImages.SetImageSize(const Value: TSysImageListSize);
begin
  FImageSize := Value;
  RecreateHandle;
end;


{$IFNDEF COMPILER_7_UP}
{ TStringListEx }

function TStringListEx.GetNameValueSeparator: Char;
begin
  if FNameValueSeparator = '' then
    NameValueSeparator := '=';
  Result := FNameValueSeparator;
end;

function TStringListEx.GetValueFromIndex(Index: Integer): string;
begin
  if Index >= 0 then
    Result := Copy(Get(Index), Length(Names[Index]) + 2, MaxInt) else
    Result := '';
end;

procedure TStringListEx.SetNameValueSeparator(const Value: Char);
begin
  if (FNameValueSeparator <> Value) then
    FNameValueSeparator := Value;
end;

procedure TStringListEx.SetValueFromIndex(Index: Integer;
  const Value: string);
begin
  if Value <> '' then
  begin
    if Index < 0 then Index := Add('');
    Put(Index, Names[Index] + NameValueSeparator + Value);
  end
  else
    if Index >= 0 then Delete(Index);
end;
{$ENDIF}

initialization
  LoadShell32Functions;
  StreamHelper := TCommonMemoryStreamHelper.Create;
  MarlettFont := TFont.Create;
  MarlettFont.Name := 'marlett';
  Checks := TCommonCheckBoundManager.Create;
  PIDLMgr := TCommonPIDLManager.Create;


finalization
  if FreeShellLib then
    FreeLibrary(ShellDLL);
  StreamHelper.Free;
  FreeAndNil(Checks);
  FreeAndNil(MarlettFont);
  FLargeSysImages.Free;
  FSmallSysImages.Free;
  FExtraLargeSysImages.Free;
  FreeAndNil(PIDLMgr);

end.
