////////////////////////////////////////////////////////////////////////////////
// EXTMENUS //
////////////////////////////////////////////////////////////////////////////////
// Owner Drawn Menus for D2 & D3 //
////////////////////////////////////////////////////////////////////////////////
// Jean-Luc Mattei //
// jlucm@club-internet.fr //
////////////////////////////////////////////////////////////////////////////////
// IMPORTANT NOTICE : //
// //
// //
// This program is FreeWare //
// //
// Please do not release modified versions of this source code. //
// If you've made any changes that you think should have been there, //
// feel free to submit them to me at jlucm@club-internet.fr //
////////////////////////////////////////////////////////////////////////////////
// NOTES : //
// //
// * it's a test component... //
////////////////////////////////////////////////////////////////////////////////
unit ExMenus;
interface
uses Windows, SysUtils, Classes, Messages, Menus, DsgnIntf, Forms, Controls, StdCtrls, Graphics;
type
TDrawMenuItemEvent = procedure(Control: TMenu; Item: TMenuItem; Rect: TRect; State: TOwnerDrawState) of object;
TMeasureMenuItemEvent = procedure(Control: TMenu; Item: TMenuItem; var Height, Width: Integer) of object;
TMenuExtender = class(TComponent)
private
FCanvas: TCanvas;
FMenu: TMenu;
FFont: TFont;
FBrush: TBrush;
FItemHeight: Integer;
FItemWidth: Integer;
FTPUHandle: THandle;
FNewTPUtilWndProcInstance: Pointer;
FOldTPUtilWndProc: Pointer;
FNewTFormWndProcInstance: Pointer;
FOldTFormWndProc: Pointer;
FOnDrawItem: TDrawMenuItemEvent;
FOnMeasureItem: TMeasureMenuItemEvent;
FOwnerDrawAll: boolean;
protected
procedure SetMenu(Value: TMenu);
procedure SetOwnerDrawAll(Value : Boolean);
procedure WMDrawItem(var Message: TWMDrawItem); message WM_DRAWITEM;
procedure WMMeasureItem(var Message: TWMMeasureItem); message WM_MEASUREITEM;
procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM;
procedure CNMeasureItem(var Message: TWMMeasureItem); message CN_MEASUREITEM;
procedure DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState); virtual;
procedure MeasureItem(Index: Integer; var Height, Width: Integer); virtual;
procedure ModifyMenuTree(MenuItems : TMenuItem; OwnerDraw: Boolean);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure NewTPUtilWndProc(Var Message: TMessage);
procedure NewTFormWndProc(Var Message: TMessage);
procedure SetItem(Var Item: TMenuItem; OwnerDraw: Boolean);
published
property Menu: TMenu read FMenu write SetMenu;
property OwnerDrawAll: Boolean read FOwnerDrawAll write SetOwnerDrawAll;
property OnDrawItem: TDrawMenuItemEvent read FOnDrawItem write FOnDrawItem;
property OnMeasureItem: TMeasureMenuItemEvent read FOnMeasureItem write FOnMeasureItem;
property ItemHeigth: Integer read FItemHeight write FItemHeight;
property ItemWidth: Integer read FItemWidth write FItemWidth;
property Font: TFont read FFont write FFont;
property Color: TBrush read FBrush write FBrush;
property Canvas: TCanvas read FCanvas;
end;
TColorMenu = class(TMenuExtender)
private
protected
procedure DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property ItemHeigth: Integer read FItemHeight write FItemHeight;
property ItemWidth: Integer read FItemWidth write FItemWidth;
end;
procedure Register;
implementation
const FTPUCount: Integer = 0;
{ TMenuExtender }
constructor TMenuExtender.Create(AOwner: TComponent);
Var i: Integer;
TmpHandle: THandle;
ProcessId: Pointer;
TmpProcessId: Pointer;
CName: Array [0..50] of Char;
begin
// TPUtilWindow is a @#!!##@& window created by delphi
// It's defaultProc Eats WM_DRAWITEM and WM_MEASUREITEM from Menus !!! ???
// So We Have to Override this problem like this :
//GetDeskTopWindow
inherited Create(AOwner);
// Get the Handle of this @#!!##@& window
if Not ( csDesigning in ComponentState ) then begin
Inc(FTPUCount);
if ( FTPUCount = 1 ) then begin
FTPUHandle:= FindWindow('TPUtilWindow', '');
FTPUHandle:= GetNextWindow(FTPUHandle, GW_HWNDPREV);
GetWindowThreadProcessId(Application.Handle, @ProcessId);
TmpHandle:= FTPUHandle;
repeat
FTPUHandle:= GetNextWindow(FTPUHandle, GW_HWNDNEXT);
Windows.GetClassName(FTPUHandle, CName, 50);
if ( strcomp( CName, 'TPUtilWindow') = 0 ) then begin
GetWindowThreadProcessId(FTPUHandle, @TmpProcessId);
end
else
TmpProcessId:= nil;
until ( ( FTPUHandle = 0 ) or ( TmpHandle = FTPUHandle ) or ( ProcessId = TmpProcessId ) );
if ( FTPUHandle <> 0 ) and ( ProcessId = TmpProcessId ) then begin
// Create a new proc pointer
FNewTPUtilWndProcInstance := MakeObjectInstance(NewTPUtilWndProc);
// Keep the old DefaultProc
FOldTPUtilWndProc := Pointer(GetWindowLong(FTPUHandle, GWL_WNDPROC));
// Set the new Proc
SetWindowLong(FTPUHandle, GWL_WNDPROC, Longint(FNewTPUtilWndProcInstance));
end
else Dec(FTPUCount);
end;
// if We have a main menu we need to intercept TForm messages
if ( AOwner is TForm ) then begin
FNewTFormWndProcInstance := MakeObjectInstance(NewTFormWndProc);
// Keep the old DefaultProc
FOldTFormWndProc := Pointer(GetWindowLong((Owner as TForm).Handle, GWL_WNDPROC));
// Set the new Proc
SetWindowLong((Owner as TForm).Handle, GWL_WNDPROC, Longint(FNewTFormWndProcInstance));
end;
end;
FMenu:= nil;
FCanvas:= TCanvas.Create;
FItemHeight:= 15;
FItemWidth:= 100;
FOwnerDrawAll:= False;
FFont:= TFont.Create;
FBrush:= TBrush.Create;
FBrush.Color:= clBtnFace;
FOnDrawItem:= nil;
FOnMeasureItem:= nil;
end;
destructor TMenuExtender.Destroy;
begin
FOnDrawItem:= nil;
FOnMeasureItem:= nil;
FOwnerDrawAll:= False;
Menu:= nil;
//Menu:= nil;
if Not ( csDesigning in ComponentState ) then begin
Dec(FTPUCount);
if (FTPUCount = 0 ) then begin
// We have to set back the old default Proc
SetWindowLong(FTPUHandle, GWL_WNDPROC, Longint(FOldTPUtilWndProc));
// and to release Our WndProc instance
FreeObjectInstance(FNewTPUtilWndProcInstance);
end;
if ( Owner is TForm ) then begin
//We have to set back the old default Proc of the form
SetWindowLong(FTPUHandle, GWL_WNDPROC, Longint(FOldTFormWndProc));
// and to release Our WndProc instance
FreeObjectInstance(FNewTFormWndProcInstance);
end;
end;
FFont.Free;
FBrush.Free;
FCanvas.Free;
FMenu:= nil;
inherited Destroy;
end;
procedure TMenuExtender.ModifyMenuTree(MenuItems : TMenuItem; OwnerDraw: Boolean);
Var i: Integer;
begin
if ( csDesigning in ComponentState ) then Exit;
for i:= 0 to MenuItems.Count - 1 do begin
ModifyMenuTree(MenuItems, OwnerDraw);
if ( OwnerDraw ) then begin
if ( MenuItems.Break <> mbBarBreak ) then
ModifyMenu(FMenu.Handle, MenuItems.Command, MF_BYCOMMAND or MF_OWNERDRAW, MenuItems.Command, Pointer(Self))
else
ModifyMenu(FMenu.Handle, MenuItems.Command, MF_BYCOMMAND or MF_OWNERDRAW or MF_MENUBREAK, MenuItems.Command, Pointer(Self));
end
else begin
if ( MenuItems.Break <> mbBarBreak ) then
ModifyMenu(FMenu.Handle, MenuItems.Command, MF_BYCOMMAND or MF_OWNERDRAW, MenuItems.Command, Pointer(Self))
else
ModifyMenu(FMenu.Handle, MenuItems.Command, MF_BYCOMMAND or MF_OWNERDRAW or MF_MENUBREAK, MenuItems.Command, Pointer(Self));
end;
end;
end;
procedure TMenuExtender.SetItem(Var Item: TMenuItem; OwnerDraw: Boolean);
begin
if ( FMenu <> nil ) then begin
if ( OwnerDraw ) then begin
if ( Item.Break <> mbBarBreak ) then
ModifyMenu(FMenu.Handle, Item.Command, MF_BYCOMMAND or MF_OWNERDRAW, Item.Command, Pointer(Self))
else
ModifyMenu(FMenu.Handle, Item.Command, MF_BYCOMMAND or MF_OWNERDRAW or MF_MENUBREAK, Item.Command, Pointer(Self));
end
else begin
if ( Item.Break <> mbBarBreak ) then
ModifyMenu(FMenu.Handle, Item.Command, MF_BYCOMMAND or MF_OWNERDRAW, Item.Command, Pointer(Self))
else
ModifyMenu(FMenu.Handle, Item.Command, MF_BYCOMMAND or MF_OWNERDRAW or MF_MENUBREAK, Item.Command, Pointer(Self));
end;
end;
end;
procedure TMenuExtender.SetOwnerDrawAll(Value : Boolean);
begin
if ( FOwnerDrawAll <> Value ) then begin
FOwnerDrawAll:= Value;
if ( FMenu <> nil ) then
ModifyMenuTree(FMenu.Items, FOwnerDrawAll);
end;
end;
procedure TMenuExtender.SetMenu(Value: TMenu);
Var i: Integer;
begin
if ( FMenu <> Value ) then begin
if ( FMenu <> nil ) and ( Value = nil ) then begin
ModifyMenuTree(FMenu.Items, False);
end
else begin
if ( OwnerDrawAll ) then begin
if ( FMenu = nil ) and ( Value <> nil ) then begin
FMenu:= Value;
ModifyMenuTree(FMenu.Items, True);
end;
end;
end;
FMenu:= Value;
end;
end;
procedure TMenuExtender.WMDrawItem(var Message: TWMDrawItem);
begin
CNDrawItem(Message);
end;
procedure TMenuExtender.CNDrawItem(var Message: TWMDrawItem);
var State : TOwnerDrawState;
SavedDC: Integer;
begin
with Message.DrawItemStruct^ do begin
State := TOwnerDrawState(WordRec(LongRec(itemState).Lo).Lo);
SavedDC:= SaveDC(hDC);
FCanvas.Handle:= hDC;
FCanvas.Font:= FFont;
FCanvas.Brush:= FBrush;
if (Integer(itemID) >= 0) and (odSelected in State) then begin
FCanvas.Brush.Color := clHighlight;
FCanvas.Font.Color := clHighlightText
end;
if Integer(itemID) >= 0 then
DrawItem(itemID, rcItem, State)
else
FCanvas.FillRect(rcItem);
if odFocused in State then
DrawFocusRect(hDC, rcItem);
FCanvas.Handle := 0;
RestoreDC(hDC, SavedDC);
end;
Message.Result:= longint(true);
end;
procedure TMenuExtender.WMMeasureItem(var Message: TWMMeasureItem);
begin
CNMeasureItem(Message);
end;
procedure TMenuExtender.CNMeasureItem(var Message: TWMMeasureItem);
begin
with Message.MeasureItemStruct^ do begin
itemHeight := FItemHeight;
itemWidth := FItemWidth;
MeasureItem(itemID, Integer(itemHeight), Integer(itemWidth));
Message.Result:= longint(true);
end;
end;
procedure TMenuExtender.NewTPUtilWndProc(Var Message: TMessage);
begin
// Only used to handle WM_DRAWITEM and WM_MEASUREITEM from Menus
//if ( FMenu <> nil ) then begin
case Message.Msg of
WM_DRAWITEM : if ( TWMDrawItem(Message).DrawItemStruct^.CtlType = ODT_MENU ) then begin
if ( TWMDrawItem(Message).DrawItemStruct^.itemData <> 0 ) then begin
TMenuExtender(TWMDrawItem(Message).DrawItemStruct^.itemData).Dispatch(Message);
end;
end;
WM_MEASUREITEM : if ( TWMMeasureItem(Message).idCtl = 0 ) then begin
if ( TWMMeasureItem(Message).MeasureItemStruct^.itemData <> 0 ) then begin
TMenuExtender(TWMMeasureItem(Message).MeasureItemStruct^.itemData).Dispatch(Message);
end;
end;
end;
//end;
// else we call the old DefaultProc.
Message.Result:= CallWindowProc(FOldTPUtilWndProc, FTPUHandle, Message.Msg, Message.WParam, Message.LParam);
end;
procedure TMenuExtender.NewTFormWndProc(Var Message: TMessage);
begin
// Only used to handle WM_DRAWITEM and WM_MEASUREITEM from Menus
if ( FMenu <> nil ) then begin
case Message.Msg of
WM_DRAWITEM : if ( TWMDrawItem(Message).DrawItemStruct^.CtlType = ODT_MENU ) then begin
if ( TWMDrawItem(Message).DrawItemStruct^.itemData <> 0 ) then begin
TMenuExtender(TWMDrawItem(Message).DrawItemStruct^.itemData).Dispatch(Message);
end;
end;
WM_MEASUREITEM : if ( TWMMeasureItem(Message).idCtl = 0 ) then begin
if ( TWMMeasureItem(Message).MeasureItemStruct^.itemData <> 0 ) then begin
TMenuExtender(TWMMeasureItem(Message).MeasureItemStruct^.itemData).Dispatch(Message);
end;
end;
end;
end;
// else we call the old DefaultProc.
Message.Result:= CallWindowProc(FOldTFormWndProc, (Owner as TForm).Handle, Message.Msg, Message.WParam, Message.LParam);
end;
procedure TMenuExtender.MeasureItem(Index: Integer; var Height, Width: Integer);
Var Item: TMenuItem;
begin
Item:= FMenu.FindItem(Index, fkCommand);
if Assigned(FOnMeasureItem) then
FOnMeasureItem(FMenu, Item, Height, Width)
end;
procedure TMenuExtender.DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState);
Var Item: TMenuItem;
Mark: String[5];
C : Array[0..100] of char;
begin
Item:= FMenu.FindItem(Index, fkCommand);
if Assigned(FOnDrawItem) then
FOnDrawItem(FMenu, Item, Rect, State)
else
begin
FCanvas.FillRect(Rect);
Mark:= '';
Rect.Left:= Rect.Left + 5;
if ( Item.Checked ) then
if ( Item.RadioItem ) then
Mark:= '*'
else
Mark:= '+';
if ( Item.Default ) then
Canvas.Font.Style:= [fsBold];
if Not ( ( FMenu is TMainMenu ) and ( Fmenu.Items.IndexOf(Item) <> -1 ) ) then begin
DrawText(Canvas.Handle, StrPCopy(C, Mark), -1, Rect, DT_LEFT or DT_VCENTER or DT_SINGLELINE);
Rect.Left:= Rect.Left + 20;
end;
DrawText(Canvas.Handle, StrPCopy(C, Item.Caption), -1, Rect, DT_LEFT or DT_VCENTER or DT_SINGLELINE);
end;
end;
constructor TColorMenu.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
OwnerDrawAll:= True;
end;
destructor TColorMenu.Destroy;
begin
inherited destroy;
end;
procedure TColorMenu.DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState);
Var Item: TMenuItem;
begin
Item:= FMenu.FindItem(Index, fkCommand);
InflateRect(Rect, 0, -1);
case Index of
0 : Canvas.Brush.Color:= clBlack;
1 : Canvas.Brush.Color:= clMaroon;
2 : Canvas.Brush.Color:= clGreen;
3 : Canvas.Brush.Color:= clOlive;
4 : Canvas.Brush.Color:= clNavy;
5 : Canvas.Brush.Color:= clPurple;
6 : Canvas.Brush.Color:= clTeal;
7 : Canvas.Brush.Color:= clGray;
8 : Canvas.Brush.Color:= clSilver;
9 : Canvas.Brush.Color:= clRed;
10 : Canvas.Brush.Color:= clLime;
11 : Canvas.Brush.Color:= clBlue;
12 : Canvas.Brush.Color:= clFuchsia;
13 : Canvas.Brush.Color:= clAqua;
14 : Canvas.Brush.Color:= clWhite;
end;
Canvas.FillRect(Rect);
Canvas.Pen.Color:= clBtnShadow;
Canvas.MoveTo(Rect.Left, Rect.Bottom);
Canvas.LineTo(Rect.Left, Rect.Top);
Canvas.LineTo(Rect.Right, Rect.Top);
Canvas.Pen.Color:= clBtnHighLight;
Canvas.MoveTo(Rect.Right, Rect.Top);
Canvas.LineTo(Rect.Right, Rect.Bottom);
Canvas.LineTo(Rect.Left, Rect.Bottom);
if (Item.Checked) then begin
Canvas.Pen.Width:= 3;
Canvas.Pen.Color:= clBlack;
Canvas.MoveTo(Rect.Left+2, Rect.Top+2);
Canvas.LineTo(Rect.Right-2, Rect.Bottom - 2);
Canvas.MoveTo(Rect.Left+2, Rect.Bottom-2);
Canvas.LineTo(Rect.Right-2, Rect.Top+2);
Canvas.Pen.Width:= 1;
Canvas.Pen.Color:= Canvas.Brush.Color;
Canvas.Brush.Style:= bsClear;
Canvas.Rectangle(Rect.Left+1, Rect.Top+1, Rect.Right-1, Rect.Bottom-1);
end;
end;
procedure Register;
begin
RegisterComponents('Exemples', [TMenuExtender]);
RegisterComponents('Exemples', [TColorMenu]);
end;
end.
一个现成的资料,你慢慢看吧