📄 jsimagelistxp.pas
字号:
var
I: Integer;
begin
FChanged := True;
if FUpdateCount > 0 then
Exit;
if FClients <> nil then
for I := 0 to FClients.Count - 1 do
TChangeLink(FClients[I]).Change;
if Assigned(FOnChange) then
FOnChange(Self);
end;
procedure TCustomImageList.UnRegisterChanges(Value: TChangeLink);
var
I: Integer;
begin
if FClients <> nil then
for I := 0 to FClients.Count - 1 do
if FClients[I] = Value then
begin
Value.Sender := nil;
FClients.Delete(I);
Break;
end;
end;
procedure TCustomImageList.RegisterChanges(Value: TChangeLink);
begin
Value.Sender := Self;
if FClients <> nil then
FClients.Add(Value);
end;
function TCustomImageList.Equal(IL: TCustomImageList): Boolean;
function StreamsEqual(S1, S2: TMemoryStream): Boolean;
begin
Result := (S1.Size = S2.Size) and CompareMem(S1.Memory, S2.Memory, S1.Size);
end;
var
MyImage, OtherImage: TMemoryStream;
begin
if (IL = nil) or (Count <> IL.Count) then
begin
Result := False;
Exit;
end;
if (Count = 0) and (IL.Count = 0) then
begin
Result := True;
Exit;
end;
MyImage := TMemoryStream.Create;
try
WriteData(MyImage);
OtherImage := TMemoryStream.Create;
try
IL.WriteData(OtherImage);
Result := StreamsEqual(MyImage, OtherImage);
finally
OtherImage.Free;
end;
finally
MyImage.Free;
end;
end;
procedure TCustomImageList.DefineProperties(Filer: TFiler);
function DoWrite: Boolean;
begin
if Filer.Ancestor <> nil then
Result := not (Filer.Ancestor is TCustomImageList) or
not Equal(TCustomImageList(Filer.Ancestor))
else
Result := Count > 0;
end;
begin
inherited DefineProperties(Filer);
Filer.DefineBinaryProperty('Bitmap', ReadData, WriteData, DoWrite);
end;
procedure TCustomImageList.ReadD2Stream(Stream: TStream);
var
FullImage, Image, FullMask, Mask: TBitmap;
I, J, Size, Pos, Count: Integer;
SrcRect: TRect;
begin
Stream.ReadBuffer(Size, SizeOf(Size));
Stream.ReadBuffer(Count, SizeOf(Count));
FullImage := TBitmap.Create;
try
Pos := Stream.Position;
FullImage.LoadFromStream(Stream);
Stream.Position := Pos + Size;
FullMask := TBitmap.Create;
try
FullMask.LoadFromStream(Stream);
Image := TBitmap.Create;
Image.Width := Width;
Image.Height := Height;
Mask := TBitmap.Create;
Mask.Monochrome := True;
Mask.Width := Width;
Mask.Height := Height;
SrcRect := Rect(0, 0, Width, Height);
BeginUpdate;
try
for J := 0 to (FullImage.Height div Height) - 1 do
begin
if Count = 0 then
Break;
for I := 0 to (FullImage.Width div Width) - 1 do
begin
if Count = 0 then
Break;
Image.Canvas.CopyRect(SrcRect, FullImage.Canvas,
Bounds(I * Width, J * Height, Width, Height));
Mask.Canvas.CopyRect(SrcRect, FullMask.Canvas,
Bounds(I * Width, J * Height, Width, Height));
Add(Image, Mask);
Dec(Count);
end;
end;
finally
Image.Free;
Mask.Free;
EndUpdate;
end;
finally
FullMask.Free;
end;
finally
FullImage.Free;
end;
end;
procedure TCustomImageList.ReadD3Stream(Stream: TStream);
var
LAdapter: TStreamAdapter;
LTemp: TMemoryStream;
LRetry: Boolean;
LValue, LBitCount: Byte;
begin
// attempt a simple read
LAdapter := TStreamAdapter.Create(Stream);
try
Handle := ImageList_Read(LAdapter);
finally
LAdapter.Free;
end;
// if we were not successful then attempt to fix up the really old ComCtl stream
if not HandleAllocated then
begin
// make a temp copy of the stream
LRetry := False;
LTemp := TMemoryStream.Create;
try
Stream.Position := 0;
LTemp.LoadFromStream(Stream);
// find the bad value imagelist header info
LTemp.Position := 18;
if (LTemp.Read(LValue, 1) = 1) and (LValue = 1) then
begin
// find the bitcount data farther on into the BitmapInfoHeader
LTemp.Position := 56;
if LTemp.Read(LBitCount, 1) = 1 then
begin
// correct the original value
LValue := LValue or LBitCount;
// back to the imagelist header info and patch it
LTemp.Position := 18;
LRetry := LTemp.Write(LValue, 1) = 1;
end;
end;
// reattempt the load
if LRetry then
begin
LTemp.Position := 0;
LAdapter := TStreamAdapter.Create(LTemp);
try
Handle := ImageList_Read(LAdapter);
finally
LAdapter.Free;
end;
end;
finally
LTemp.Free;
end;
// if we still didn't succeed then fail
if not HandleAllocated then
raise EReadError.CreateRes(@SImageReadFail);
end;
end;
procedure TCustomImageList.ReadData(Stream: TStream);
var
CheckInt1, CheckInt2: Integer;
CheckByte1, CheckByte2: Byte;
StreamPos: Integer;
begin
FreeHandle;
StreamPos := Stream.Position; // check stream signature to
Stream.Read(CheckInt1, SizeOf(CheckInt1)); // determine a Delphi 2 or Delphi
Stream.Read(CheckInt2, SizeOf(CheckInt2)); // 3 imagelist stream. Delphi 2
CheckByte1 := Lo(LoWord(CheckInt1)); // streams can be read, but only
CheckByte2 := Hi(LoWord(CheckInt1)); // Delphi 3 streams will be written
Stream.Position := StreamPos;
if (CheckInt1 <> CheckInt2) and (CheckByte1 = $49) and (CheckByte2 = $4C) then
ReadD3Stream(Stream)
else
ReadD2Stream(Stream);
end;
const
ComCtlVersionIE6 = $00060000;
var
CachedComCtrlVer: Cardinal;
ImageListWriteExProc: function(ImageList: HIMAGELIST; Flags: DWORD; Stream:
IStream): HRESULT; stdcall;
procedure TCustomImageList.WriteData(Stream: TStream);
var
SA: TStreamAdapter;
ComCtrlHandle: THandle;
const
ILP_DOWNLEVEL = 1;
begin
if CachedComCtrlVer = 0 then
begin
CachedComCtrlVer := GetFileVersion(comctl32);
if CachedComCtrlVer >= ComCtlVersionIE6 then
begin
ComCtrlHandle := GetModuleHandle(comctl32);
if ComCtrlHandle <> 0 then
ImageListWriteExProc := GetProcAddress(ComCtrlHandle,
'ImageList_WriteEx'); { Do not localize }
end;
end;
SA := TStreamAdapter.Create(Stream);
try
{ See if we should use the new API for writing image lists in the old
format. }
if Assigned(ImageListWriteExProc) then
begin
if ImageListWriteExProc(Handle, ILP_DOWNLEVEL, SA) <> S_OK then
raise EWriteError.CreateRes(@SImageWriteFail)
end
else if not ImageList_Write(Handle, SA) then
raise EWriteError.CreateRes(@SImageWriteFail);
finally
SA.Free;
end;
end;
(*
var
I: Integer;
DIB1, DIB2: TBitmap;
DC: HDC;
S: TMemoryStream;
procedure WriteDIB(BM: HBitmap);
{ The ImageList leaves its bitmap handle selected into a DC somewhere,
so we can't select it into our own DC to copy from it. The only safe
operation is GetDIB (GetDIBits), which extracts the pixel bits without
selecting the BM into a DC. This code builds our own bitmap from
those bits, then crops it to the minimum size before writing it out.}
var
BitsSize: DWORD;
Header, Bits: PChar;
DIBBits: Pointer;
R: TRect;
HeaderSize: DWORD;
GlyphsPerRow, Rows: Integer;
begin
if BM = 0 then Exit;
GetDIBSizes(BM, HeaderSize, BitsSize);
GetMem(Header, HeaderSize + BitsSize);
try
Bits := Header + HeaderSize;
GetDIB(BM, 0, Header^, Bits^);
DIB1.Handle := CreateDIBSection(DC, PBitmapInfo(Header)^, DIB_RGB_COLORS, DIBBits, 0, 0);
System.Move(Bits^, DIBBits^, BitsSize);
with PBitmapInfo(Header)^.bmiHeader do
begin
GlyphsPerRow := biWidth div Width;
if GlyphsPerRow = 0 then Inc(GlyphsPerRow);
if GlyphsPerRow > Count then GlyphsPerRow := Count;
biWidth := GlyphsPerRow * Width;
Rows := Count div GlyphsPerRow;
if Count > Rows * GlyphsPerRow then Inc(Rows);
biHeight := Rows * Height;
R := Rect(0, 0, biWidth, biHeight);
end;
DIB2.Handle := CreateDIBSection(DC, PBitmapInfo(Header)^, DIB_RGB_COLORS, DIBBits, 0, 0);
DIB2.Canvas.CopyRect(R, DIB1.Canvas, R);
DIB2.SaveToStream(S);
finally
FreeMem(Header);
end;
end;
begin
DIB1 := nil;
DIB2 := nil;
DC := 0;
S := TMemoryStream.Create;
try
DIB1 := TBitmap.Create;
DIB2 := TBitmap.Create;
DC := GetDC(0);
WriteDIB(GetImageBitmap);
I := S.Size;
WriteDIB(GetMaskBitmap);
Stream.WriteBuffer(I, sizeof(I));
I := Count;
Stream.WriteBuffer(I, sizeof(I));
Stream.WriteBuffer(S.Memory^, S.Size);
finally
ReleaseDC(0, DC);
DIB1.Free;
DIB2.Free;
S.Free;
end;
end;
*)
procedure TCustomImageList.BeginUpdate;
begin
Inc(FUpdateCount);
end;
procedure TCustomImageList.EndUpdate;
begin
if FUpdateCount > 0 then
Dec(FUpdateCount);
if FChanged then
begin
FChanged := False;
Change;
end;
end;
{ TChangeLink }
destructor TChangeLink.Destroy;
begin
if Sender <> nil then
Sender.UnRegisterChanges(Self);
inherited Destroy;
end;
procedure TChangeLink.Change;
begin
if Assigned(OnChange) then
OnChange(Sender);
end;
function ClientToWindow(Handle: HWND; X, Y: Integer): TPoint;
var
Rect: TRect;
Point: TPoint;
begin
Point.X := X;
Point.Y := Y;
ClientToScreen(Handle, Point);
GetWindowRect(Handle, Rect);
Result.X := Point.X - Rect.Left;
Result.Y := Point.Y - Rect.Top;
end;
procedure TDragImageList.Initialize;
begin
inherited Initialize;
DragCursor := crNone;
end;
function TDragImageList.SetDragImage(Index, HotSpotX, HotSpotY: Integer):
Boolean;
begin
if HandleAllocated then
begin
FDragIndex := Index;
FDragHotspot.x := HotSpotX;
FDragHotspot.y := HotSpotY;
ImageList_BeginDrag(Handle, Index, HotSpotX, HotSpotY);
Result := True;
FDragging := Result;
end
else
Result := False;
end;
procedure TDragImageList.SetDragCursor(Value: TCursor);
begin
if Value <> DragCursor then
begin
FDragCursor := Value;
if Dragging then
Screen.Cursor := DragCursor;
end;
end;
function TDragImageList.GetHotSpot: TPoint;
begin
Result := inherited GetHotSpot;
if HandleAllocated and Dragging then
ImageList_GetDragImage(nil, @Result);
end;
function TDragImageList.BeginDrag(Window: HWND; X, Y: Integer): Boolean;
begin
Result := False;
if HandleAllocated then
begin
if not Dragging then
SetDragImage(FDragIndex, FDragHotspot.x, FDragHotspot.y);
Result := DragLock(Window, X, Y);
if Result then
begin
FOldCursor := Screen.Cursor;
Screen.Cursor := DragCursor;
end;
end;
end;
function TDragImageList.DragLock(Window: HWND; XPos, YPos: Integer): Boolean;
begin
Result := False;
if HandleAllocated and (Window <> FDragHandle) then
begin
DragUnlock;
FDragHandle := Window;
with ClientToWindow(FDragHandle, XPos, YPos) do
Result := ImageList_DragEnter(FDragHandle, X, Y);
end;
end;
procedure TDragImageList.DragUnlock;
begin
if HandleAllocated and (FDragHandle <> 0) then
begin
ImageList_DragLeave(FDragHandle);
FDragHandle := 0;
end;
end;
function TDragImageList.DragMove(X, Y: Integer): Boolean;
begin
if HandleAllocated then
with ClientToWindow(FDragHandle, X, Y) do
Result := ImageList_DragMove(X, Y)
else
Result := False;
end;
procedure TDragImageList.ShowDragImage;
begin
if HandleAllocated then
ImageList_DragShowNoLock(True);
end;
procedure TDragImageList.HideDragImage;
begin
if HandleAllocated then
ImageList_DragShowNoLock(False);
end;
function TDragImageList.EndDrag: Boolean;
begin
if HandleAllocated and Dragging then
begin
DragUnlock;
Result := ImageList_EndDrag;
FDragging := False;
DragCursor := crNone;
Screen.Cursor := FOldCursor;
end
else
Result := False;
end;
end.
(*unit JSImageListXP;
interface
uses
SysUtils,
Classes,
ImgList,
Controls,
CommCtrl,
Consts;
type
TJSImageListXP = class(TJSImageListXP)
private
procedure ConvertTo32BitImageList(const ImageList: TJSImageListXP);
protected
{ Protected declarations }
public
constructor Create(AOwner: TComponent); override;
published
{ Published declarations }
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Jerk System', [TJSImageListXP]);
end;
{ TJSImageListXP }
procedure TJSImageListXP.ConvertTo32BitImageList(const ImageList: TJSImageListXP);
const
Mask: array[Boolean] of Longint = (0, ILC_MASK);
var
TempList: TJSImageListXP;
begin
if Assigned(ImageList) then
begin
TempList := TJSImageListXP.Create(nil);
try
TempList.Assign(ImageList);
with ImageList do
begin
Handle := ImageList_Create(
Width, Height, ILC_COLOR32 or Mask[Masked], 0, AllocBy);
if not HandleAllocated then
raise EInvalidOperation.Create(SInvalidImageList);
end;
Imagelist.AddImages(TempList);
finally
FreeAndNil(TempList);
end;
end;
end;
constructor TJSImageListXP.Create(AOwner: TComponent);
begin
inherited;
ConvertTo32BitImageList(Self);
end;
end.
*)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -