lazarus/lcl/interfaces/win32/win32wsmenus.pp
marc 04b4e27b62 * Implemented basic alpha support
* Implemented LCL side of imagelist
* restructured rawimage to more OO

Merged revisions 11289-11617 via svnmerge from 
http://svn.freepascal.org/svn/lazarus/branches/marc-lcl

........
  r11289 | marc | 2007-06-06 22:50:05 +0200 (Wed, 06 Jun 2007) | 1 line
  
  private branch for bitmap rework
........
  r11290 | marc | 2007-06-06 23:30:09 +0200 (Wed, 06 Jun 2007) | 2 lines
  
  * Initial linux and win32 implementation
........
  r11291 | paul | 2007-06-07 03:20:11 +0200 (Thu, 07 Jun 2007) | 3 lines
  
  - fix compilation with fpc 2.3.1
  - remove unneded code for converting cursor mask
  - enabled loading of standard windows status icons instead of LCL
........
  r11292 | paul | 2007-06-07 11:03:27 +0200 (Thu, 07 Jun 2007) | 1 line
  
  - some bugs with mask and alpha
........
  r11299 | marc | 2007-06-08 00:59:26 +0200 (Fri, 08 Jun 2007) | 2 lines
  
  * force alpha channel when PNG has alpha
........
  r11302 | paul | 2007-06-09 04:45:12 +0200 (Sat, 09 Jun 2007) | 1 line
  
  - fix black rectangles instead of manu item images
........
  r11303 | paul | 2007-06-09 04:46:14 +0200 (Sat, 09 Jun 2007) | 1 line
  
  formatting
........
  r11309 | marc | 2007-06-11 02:25:07 +0200 (Mon, 11 Jun 2007) | 3 lines
  
  * Added alpha premultiply
  * Published Colorbox selection property
........
  r11310 | paul | 2007-06-11 19:10:18 +0200 (Mon, 11 Jun 2007) | 1 line
  
  misc
........
  r11312 | marc | 2007-06-12 01:44:03 +0200 (Tue, 12 Jun 2007) | 2 lines
  
  * start with carbon
........
  r11313 | paul | 2007-06-12 14:02:48 +0200 (Tue, 12 Jun 2007) | 1 line
  
  - BitBtn glyph transparency
........
  r11315 | paul | 2007-06-13 05:20:40 +0200 (Wed, 13 Jun 2007) | 1 line
  
  - problems with internal bitmap saving/loading (is was 24bpp when 32bpp needed)
........
  r11319 | paul | 2007-06-14 06:32:04 +0200 (Thu, 14 Jun 2007) | 1 line
  
  - More LCL way of painting images through ThemeServices
........
  r11320 | paul | 2007-06-14 06:32:56 +0200 (Thu, 14 Jun 2007) | 1 line
  
  - ability to override bitbtn glyph to nothing
........
  r11321 | paul | 2007-06-14 06:34:49 +0200 (Thu, 14 Jun 2007) | 1 line
  
  painting headercontrol images through ThemeServices
........
  r11325 | paul | 2007-06-17 10:14:27 +0200 (Sun, 17 Jun 2007) | 1 line
  
  fixing painting of 32bpp bitmaps with no Alpha
........
  r11326 | paul | 2007-06-17 10:16:00 +0200 (Sun, 17 Jun 2007) | 1 line
  
  missed file
........
  r11337 | paul | 2007-06-20 03:44:47 +0200 (Wed, 20 Jun 2007) | 3 lines
  
  - revert previous commit
  - create 24bpp bitmaps by default
........
  r11342 | marc | 2007-06-21 01:47:30 +0200 (Thu, 21 Jun 2007) | 3 lines
  
  * Added Alpha support on Carbon
  * Simplified win32 rawimage_fromdevice
........
  r11343 | paul | 2007-06-21 04:36:28 +0200 (Thu, 21 Jun 2007) | 1 line
  
  - adopt gtk2 code
........
  r11344 | paul | 2007-06-21 04:41:41 +0200 (Thu, 21 Jun 2007) | 1 line
  
  make gtk2 work
........
  r11353 | paul | 2007-06-22 10:12:19 +0200 (Fri, 22 Jun 2007) | 1 line
  
  - default WS imagelist implementation
........
  r11358 | marc | 2007-06-23 13:29:06 +0200 (Sat, 23 Jun 2007) | 2 lines
  
  * Implemented MaskBlit
........
  r11359 | paul | 2007-06-23 20:02:52 +0200 (Sat, 23 Jun 2007) | 1 line
  
  draw new imagelist bitmap on widget canvas
........
  r11371 | marc | 2007-06-25 23:50:13 +0200 (Mon, 25 Jun 2007) | 2 lines
  
  * Rawimage rework
........
  r11372 | marc | 2007-06-25 23:51:00 +0200 (Mon, 25 Jun 2007) | 2 lines
  
  + Added header
........
  r11373 | marc | 2007-06-26 00:05:55 +0200 (Tue, 26 Jun 2007) | 2 lines
  
  * Swapped RGBA <-> ARGB defualt format since most widgetsets use ARGB
........
  r11374 | marc | 2007-06-26 00:09:36 +0200 (Tue, 26 Jun 2007) | 2 lines
  
  * added
........
  r11462 | marc | 2007-07-12 00:16:02 +0200 (Thu, 12 Jul 2007) | 2 lines
  
  + added header
........
  r11463 | marc | 2007-07-12 00:18:49 +0200 (Thu, 12 Jul 2007) | 2 lines
  
  * Added alpha/masked strechblt support
........
  r11464 | marc | 2007-07-12 00:21:27 +0200 (Thu, 12 Jul 2007) | 2 lines
  
  * create DIBSection instead of DIBitmap
........
  r11502 | marc | 2007-07-14 00:23:42 +0200 (Sat, 14 Jul 2007) | 2 lines
  
  * Fixed transparentcolor after loading bitmap
........
  r11505 | marc | 2007-07-14 15:10:56 +0200 (Sat, 14 Jul 2007) | 2 lines
  
  - Removed ARGB dataconversion, internal format is by default the same now
........
  r11531 | marc | 2007-07-17 01:23:34 +0200 (Tue, 17 Jul 2007) | 2 lines
  
  * changed TRawImage into object
........
  r11533 | paul | 2007-07-17 05:10:31 +0200 (Tue, 17 Jul 2007) | 3 lines
  
  - change several occurrence of FillChar(..TRawImageDescription..) with call to Init
  - change imagelist defines to use old imagelist (new is crashes ide)
  - change TWin32ThemeServices to use old imagelist
........
  r11534 | paul | 2007-07-17 05:19:02 +0200 (Tue, 17 Jul 2007) | 3 lines
  
  - change several occurrence of FillChar(..TRawImageDescription..) with call to Init in Qt widgetset
  - change TRawImageDescription.IsEqual and TRawImage.IsEqual
........
  r11535 | paul | 2007-07-17 05:23:53 +0200 (Tue, 17 Jul 2007) | 1 line
  
  - change several occurrence of FillChar(..TRawImageDescription..) with call to Init in wince widgetset
........
  r11554 | marc | 2007-07-18 00:10:11 +0200 (Wed, 18 Jul 2007) | 2 lines
  
  * fixed compilation on 2.0.4
........
  r11555 | marc | 2007-07-18 00:10:44 +0200 (Wed, 18 Jul 2007) | 2 lines
  
  * fixed compilation
........
  r11556 | marc | 2007-07-18 00:11:43 +0200 (Wed, 18 Jul 2007) | 2 lines
  
  * fixed fillchar on TRawImage object
........
  r11572 | marc | 2007-07-19 01:41:35 +0200 (Thu, 19 Jul 2007) | 2 lines
  
  * fixed crash when object has vmt
........
  r11573 | marc | 2007-07-19 01:42:14 +0200 (Thu, 19 Jul 2007) | 2 lines
  
  * Made TRawimage compatible with record again
........
  r11580 | marc | 2007-07-20 01:33:20 +0200 (Fri, 20 Jul 2007) | 2 lines
  
  * enabled newimagelist
........
  r11581 | marc | 2007-07-20 01:33:48 +0200 (Fri, 20 Jul 2007) | 2 lines
  
  * fixed font
........

git-svn-id: trunk@11861 -
2007-08-25 01:49:40 +00:00

775 lines
25 KiB
ObjectPascal

{ $Id$}
{
*****************************************************************************
* Win32WSMenus.pp *
* --------------- *
* *
* *
*****************************************************************************
*****************************************************************************
* *
* This file is part of the Lazarus Component Library (LCL) *
* *
* See the file COPYING.modifiedLGPL, included in this distribution, *
* for details about the copyright. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* *
*****************************************************************************
}
unit Win32WSMenus;
{$mode objfpc}{$H+}
interface
uses
////////////////////////////////////////////////////
// I M P O R T A N T
////////////////////////////////////////////////////
// To get as little as posible circles,
// uncomment only when needed for registration
////////////////////////////////////////////////////
Graphics, Menus, Forms,
////////////////////////////////////////////////////
WSMenus, WSLCLClasses,
Windows, Controls, Classes, SysUtils, Win32Int, Win32Proc, InterfaceBase, LCLProc;
type
{ TWin32WSMenuItem }
TWin32WSMenuItem = class(TWSMenuItem)
private
protected
public
class procedure AttachMenu(const AMenuItem: TMenuItem); override;
class function CreateHandle(const AMenuItem: TMenuItem): HMENU; override;
class procedure DestroyHandle(const AMenuItem: TMenuItem); override;
class procedure SetCaption(const AMenuItem: TMenuItem; const ACaption: string); override;
class function SetCheck(const AMenuItem: TMenuItem; const Checked: boolean): boolean; override;
class procedure SetShortCut(const AMenuItem: TMenuItem; const OldShortCut, NewShortCut: TShortCut); override;
class function SetEnable(const AMenuItem: TMenuItem; const Enabled: boolean): boolean; override;
class function SetRightJustify(const AMenuItem: TMenuItem; const Justified: boolean): boolean; override;
end;
{ TWin32WSMenu }
TWin32WSMenu = class(TWSMenu)
private
protected
public
class function CreateHandle(const AMenu: TMenu): HMENU; override;
class procedure BiDiModeChanged(const AMenu: TMenu); override;
end;
{ TWin32WSMainMenu }
TWin32WSMainMenu = class(TWSMainMenu)
private
protected
public
end;
{ TWin32WSPopupMenu }
TWin32WSPopupMenu = class(TWSPopupMenu)
private
protected
public
class function CreateHandle(const AMenu: TMenu): HMENU; override;
class procedure Popup(const APopupMenu: TPopupMenu; const X, Y: integer); override;
end;
function MenuItemSize(aMenuItem: TMenuItem; aHDC: HDC): TSize;
procedure DrawMenuItem(const aMenuItem: TMenuItem; const aHDC: HDC; const aRect: Windows.RECT; const aSelected: boolean);
function FindMenuItemAccelerator(const ACharCode: char; const AMenuHandle: HMENU): integer;
implementation
uses strutils;
{ helper routines }
const
SpaceBetweenIcons = 5;
// define the size of the MENUITEMINFO structure used by older Windows
// versions (95, NT4) to keep the compatibility with them
// Since W98 the size is 48 (hbmpItem was added)
W95_MENUITEMINFO_SIZE = 44;
var
menuiteminfosize : DWORD = 0;
type
TCaptionFlags = (cfBold, cfUnderline);
TCaptionFlagsSet = set of TCaptionFlags;
(* Returns index of the character in the menu item caption that is displayed
as underlined and is therefore the hot key of the menu item.
If the caption does not contain any underlined character, 0 is returned.
If there are more "underscored" characters in the caption, the last one is returned.
Does some Windows API function exists which can do the same?
AnUnderlinedChar - character which tells that tne following character should be underlined
ACaption - menu item caption which is parsed *)
function SearchMenuItemHotKeyIndex(const AnUnderlinedChar: char; ACaption: string): integer;
var
position: integer;
begin
position := pos(AnUnderlinedChar, ACaption);
Result := 0;
// if aChar is on the last position then there is nothing to underscore, ignore this character
while (position > 0) and (position < length(ACaption)) do
begin
// two 'AnUnderlinedChar' characters together are not valid hot key, they are replaced by one
if ACaption[position + 1] <> AnUnderlinedChar then
Result := position + 1;
position := posEx(AnUnderlinedChar, ACaption, position + 2);
end;
end;
function FindMenuItemAccelerator(const ACharCode: char; const AMenuHandle: HMENU): integer;
var
MenuItemIndex: integer;
ItemInfo: MENUITEMINFO;
FirstMenuItem: TMenuItem;
SiblingMenuItem: TmenuItem;
HotKeyIndex: integer;
i: integer;
begin
Result := MakeLResult(0, 0);
MenuItemIndex := -1;
ItemInfo.cbSize := menuiteminfosize;
ItemInfo.fMask := MIIM_DATA;
if not GetMenuItemInfo(AMenuHandle, 0, true, @ItemInfo) then Exit;
FirstMenuItem := TMenuItem(ItemInfo.dwItemData);
if FirstMenuItem = nil then exit;
i := 0;
while (i < FirstMenuItem.Parent.Count) and (MenuItemIndex < 0) do
begin
SiblingMenuItem := FirstMenuItem.Parent.Items[i];
HotKeyIndex := SearchMenuItemHotKeyIndex('&', SiblingMenuItem.Caption);
if (HotKeyIndex > 0) and
(Upcase(ACharCode) = Upcase(SiblingMenuItem.Caption[HotKeyIndex])) then
MenuItemIndex := i;
inc(i);
end;
if MenuItemIndex > -1 then Result := MakeLResult(MenuItemIndex, 2)
else Result := MakeLResult(0, 0);
end;
function GetMenuItemFont(const aFlags: TCaptionFlagsSet): HFONT;
var
lf: LOGFONT;
ncm: NONCLIENTMETRICS;
begin
ncm.cbSize:= sizeof(ncm);
if SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), @ncm, 0) then
lf:= ncm.lfMenuFont
else
GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), @lf);
if cfUnderline in aFlags then lf.lfUnderline := 1
else lf.lfUnderline := 0;
if cfBold in aFlags then
begin
if lf.lfWeight<=400 then
lf.lfWeight:= lf.lfWeight + 300
else
lf.lfWeight:= lf.lfWeight + 100;
end;
Result := CreateFontIndirect(@lf);
end;
(* Get the menu item caption including shortcut *)
function CompleteMenuItemCaption(const aMenuItem: TMenuItem): string;
begin
Result := aMenuItem.Caption;
if aMenuItem.shortCut <> scNone then
Result := Result + ShortCutToText(aMenuItem.shortCut);
end;
(* Get the maximum length of the given string in pixels *)
function StringSize(const aCaption: String; const aHDC: HDC; const aDecoration:TCaptionFlagsSet): TSize;
var
oldFont: HFONT;
newFont: HFONT;
tmpRect: Windows.RECT;
AnsiBuffer: ansistring;
WideBuffer: widestring;
begin
tmpRect.right := 0;
tmpRect.left := 0;
newFont := getMenuItemFont(aDecoration);
oldFont := SelectObject(aHDC, newFont);
{$ifdef WindowsUnicodeSupport}
if UnicodeEnabledOS then
begin
WideBuffer := Utf8Decode(aCaption);
DrawTextW(aHDC, PWideChar(WideBuffer), length(WideBuffer), @TmpRect, DT_CALCRECT);
end
else
begin
AnsiBuffer := Utf8ToAnsi(aCaption);
DrawText(aHDC, pChar(AnsiBuffer), length(AnsiBuffer), @TmpRect, DT_CALCRECT);
end;
{$else}
DrawText(aHDC, pChar(aCaption), length(aCaption), @TmpRect, DT_CALCRECT);
{$endif}
SelectObject(aHDC, oldFont);
DeleteObject(newFont);
Result.cx := TmpRect.right - TmpRect.left;
Result.cy := TmpRect.Bottom - TmpRect.Top;
end;
function CheckSpace(AMenuItem: TMenuItem): integer;
var
i: integer;
begin
Result := 0;
if AMenuItem.IsInMenuBar then
begin
if AMenuItem.Checked then
Result := GetSystemMetrics(SM_CXMENUCHECK);
end
else
begin
for i := 0 to AMenuItem.Parent.Count - 1 do
begin
if AMenuItem.Parent.Items[i].Checked then
begin
Result := GetSystemMetrics(SM_CXMENUCHECK);
break;
end;
end;
end;
end;
function MenuIconWidth(const AMenuItem: TMenuItem): integer;
var
SiblingMenuItem : TMenuItem;
i : integer;
RequiredWidth: integer;
begin
Result := 0;
if AMenuItem.IsInMenuBar then
begin
if AMenuItem.HasIcon then
Result := AMenuItem.Bitmap.Width;
end
else
begin
for i := 0 to AMenuItem.Parent.Count - 1 do
begin
SiblingMenuItem := AMenuItem.Parent.Items[i];
if SiblingMenuItem.HasIcon then
begin
RequiredWidth := SiblingMenuItem.Bitmap.Width;
if RequiredWidth > Result then
Result := RequiredWidth;
end;
end;
end;
end;
function MenuItemSize(aMenuItem: TMenuItem; aHDC: HDC): TSize;
var
decoration: TCaptionFlagsSet;
minimumHeight, IconWidth: Integer;
begin
if aMenuItem.Default then
decoration := [cfBold]
else
decoration := [];
Result := StringSize(CompleteMenuItemCaption(aMenuItem), aHDC, decoration);
inc(Result.cx, CheckSpace(aMenuItem));
IconWidth := MenuIconWidth(aMenuItem);
if not aMenuItem.IsInMenuBar or (IconWidth <> 0) then
Inc(Result.cx, IconWidth + (2 * spaceBetweenIcons));
if aMenuItem.ShortCut <> scNone then
Inc(Result.cx, spaceBetweenIcons);
minimumHeight := GetSystemMetrics(SM_CYMENU);
if not aMenuItem.IsInMenuBar then
Dec(minimumHeight, 2);
if aMenuItem.IsLine then
Result.cy := 10 // it is a separator
else
begin
if aMenuItem.hasIcon and (aMenuItem.bitmap.height > Result.cy) then
Result.cy := aMenuItem.bitmap.height;
Inc(Result.cy, 2);
if Result.cy < minimumHeight then
Result.cy := minimumHeight;
end;
end;
function LeftCaptionPosition(const aMenuItemLength: integer; const anElementLength: integer; const AMenuItem: TMenuItem): integer;
var
IconWidth: Integer;
begin
IconWidth := MenuIconWidth(AMenuItem);
Result := CheckSpace(aMenuItem) + SpaceBetweenIcons;
if not aMenuItem.IsInMenuBar or (IconWidth <> 0) then
inc(Result, IconWidth + SpaceBetweenIcons);
end;
function TopPosition(const aMenuItemHeight: integer; const anElementHeight: integer): integer;
begin
Result := (aMenuItemHeight - anElementHeight) div 2;
end;
function BackgroundColorMenu(const aSelected: boolean; const aInMainMenu: boolean): COLORREF;
var
IsFlatMenu: Windows.BOOL;
begin
if aSelected then
Result := GetSysColor(COLOR_HIGHLIGHT)
// SPI_GETFLATMENU = 0x1022, it is not yet defined in the FPC
else if aInMainMenu and (SystemParametersInfo($1022, 0, @IsFlatMenu, 0)) and IsFlatMenu then // COLOR_MENUBAR is not supported on Windows version < XP
Result := GetSysColor(COLOR_MENUBAR)
else
Result := GetSysColor(COLOR_MENU);
end;
function TextColorMenu(const aSelected: boolean; const anEnabled: boolean): COLORREF;
begin
if anEnabled then
begin
if aSelected then
Result := GetSysColor(COLOR_HIGHLIGHTTEXT)
else
Result := GetSysColor(COLOR_MENUTEXT);
end else
Result := GetSysColor(COLOR_GRAYTEXT);
end;
procedure DrawSeparator(const aHDC: HDC; const aRect: Windows.RECT);
var
separatorRect: Windows.RECT;
begin
separatorRect.left := aRect.left;
separatorRect.right := aRect.right;
separatorRect.top := aRect.top + (aRect.bottom - aRect.top) div 2 - 1;
separatorRect.bottom := separatorRect.top + 2;
DrawEdge(aHDC, separatorRect, BDR_SUNKENOUTER, BF_RECT);
end;
procedure DrawMenuItemCheckMark(const aMenuItem: TMenuItem; const aHDC: HDC; const aRect: Windows.RECT; const aSelected: boolean);
var
checkMarkWidth: integer;
checkMarkHeight: integer;
hdcMem: HDC;
monoBitmap: HBITMAP;
oldBitmap: HBITMAP;
checkMarkShape: integer;
checkMarkRect: Windows.RECT;
x:Integer;
begin
hdcMem := CreateCompatibleDC(aHDC);
checkMarkWidth := GetSystemMetrics(SM_CXMENUCHECK);
checkMarkHeight := GetSystemMetrics(SM_CYMENUCHECK);
monoBitmap := CreateBitmap(checkMarkWidth, checkMarkHeight, 1, 1, nil);
oldBitmap := SelectObject(hdcMem, monoBitmap);
checkMarkRect.left := 0;
checkMarkRect.top := 0;
checkMarkRect.right := checkMarkWidth;
checkMarkRect.bottom := checkMarkHeight;
if aMenuItem.RadioItem then checkMarkShape := DFCS_MENUBULLET
else checkMarkShape := DFCS_MENUCHECK;
DrawFrameControl(hdcMem, @checkMarkRect, DFC_MENU, checkMarkShape);
if aMenuItem.GetIsRightToLeft then
x := aRect.Right - checkMarkWidth
else
x := aRect.left;
BitBlt(aHDC, x, aRect.top + topPosition(aRect.bottom - aRect.top, checkMarkRect.bottom - checkMarkRect.top), checkMarkWidth, checkMarkHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, oldBitmap);
DeleteObject(monoBitmap);
DeleteDC(hdcMem);
end;
procedure DrawMenuItemText(const aMenuItem: TMenuItem; const aHDC: HDC; aRect: Windows.RECT; const aSelected: boolean);
var
crText: COLORREF;
crBkgnd: COLORREF;
TmpLength: integer;
TmpHeight: integer;
oldFont: HFONT;
newFont: HFONT;
decoration: TCaptionFlagsSet;
shortCutText: string;
WorkRect: Windows.RECT;
IsRightToLeft: Boolean;
etoFlags: Cardinal;
dtFlags: Word;
AnsiBuffer: ansistring;
WideBuffer: widestring;
begin
crText := TextColorMenu(aSelected, aMenuItem.Enabled);
crBkgnd := BackgroundColorMenu(aSelected, aMenuItem.IsInMenuBar);
SetTextColor(aHDC, crText);
SetBkColor(aHDC, crBkgnd);
if aMenuItem.Default then
decoration := [cfBold]
else
decoration := [];
newFont := getMenuItemFont(decoration);
oldFont := SelectObject(aHDC, newFont);
IsRightToLeft := aMenuItem.GetIsRightToLeft;
etoFlags := ETO_OPAQUE;
dtFlags := 0;
if IsRightToLeft then
begin
etoFlags := etoFlags or ETO_RTLREADING;
dtFlags := dtFlags or DT_RIGHT or DT_RTLREADING;
end;
ExtTextOut(aHDC, 0, 0, etoFlags, @aRect, PChar(''), 0, nil);
TmpLength := aRect.right - aRect.left;
TmpHeight := aRect.bottom - aRect.top;
{$ifdef WindowsUnicodeSupport}
if UnicodeEnabledOS then
begin
WideBuffer := Utf8Decode(aMenuItem.Caption);
DrawTextW(aHDC, PWideChar(WideBuffer), length(WideBuffer), @WorkRect, DT_CALCRECT);
end
else
begin
AnsiBuffer := Utf8ToAnsi(aMenuItem.Caption);
DrawText(aHDC, pChar(AnsiBuffer), length(AnsiBuffer), @WorkRect, DT_CALCRECT);
end;
{$else}
DrawText(aHDC, pChar(aMenuItem.Caption), length(aMenuItem.Caption), @WorkRect, DT_CALCRECT);
{$endif}
if IsRightToLeft then
Dec(aRect.Right, leftCaptionPosition(TmpLength, WorkRect.Right - WorkRect.Left, aMenuItem))
else
Inc(aRect.Left, leftCaptionPosition(TmpLength, WorkRect.Right - WorkRect.Left, aMenuItem));
Inc(aRect.Top, topPosition(TmpHeight, WorkRect.Bottom - WorkRect.Top));
{$ifdef WindowsUnicodeSupport}
if UnicodeEnabledOS then
begin
WideBuffer := Utf8Decode(aMenuItem.Caption);
DrawTextW(aHDC, PWideChar(WideBuffer), length(WideBuffer), @aRect, dtFlags);
end
else
begin
AnsiBuffer := Utf8ToAnsi(aMenuItem.Caption);
DrawText(aHDC, pChar(AnsiBuffer), length(AnsiBuffer), @aRect, dtFlags);
end;
{$else}
DrawText(aHDC, pChar(aMenuItem.Caption), length(aMenuItem.Caption), @aRect, dtFlags);
{$endif}
if aMenuItem.ShortCut <> scNone then
begin
shortCutText := ShortCutToText(aMenuItem.ShortCut);
if IsRightToLeft then
begin
Inc(aRect.Left, GetSystemMetrics(SM_CXMENUCHECK));
dtFlags := DT_LEFT;
end
else
begin
Dec(aRect.Right, GetSystemMetrics(SM_CXMENUCHECK));
dtFlags := DT_RIGHT;
end;
{$ifdef WindowsUnicodeSupport}
if UnicodeEnabledOS then
begin
WideBuffer := Utf8Decode(shortCutText);
DrawTextW(aHDC, PWideChar(WideBuffer), length(WideBuffer), @aRect, dtFlags);
end
else
begin
AnsiBuffer := Utf8ToAnsi(shortCutText);
DrawText(aHDC, pChar(AnsiBuffer), length(AnsiBuffer), @aRect, dtFlags);
end;
{$else}
DrawText(aHDC, pChar(shortCutText), Length(shortCutText), @aRect, dtFlags);
{$endif}
end;
SelectObject(aHDC, oldFont);
DeleteObject(newFont);
end;
procedure DrawMenuItemIcon(const aMenuItem: TMenuItem; const aHDC: HDC; const aRect: Windows.RECT; const aSelected: boolean);
var
hdcMem: HDC;
hbmpOld: HBITMAP;
x: Integer;
bmp: Graphics.TBitmap;
begin
// prevent multiple creation copies of menuitem bitmap form imagelist
bmp := aMenuItem.Bitmap;
hdcMem := bmp.Canvas.Handle;
hbmpOld := SelectObject(hdcMem, bmp.Handle);
if aMenuItem.GetIsRightToLeft then
x := aRect.Right - CheckSpace(aMenuItem) - bmp.Width - spaceBetweenIcons
else
x := aRect.Left + CheckSpace(aMenuItem) + spaceBetweenIcons;
TWin32WidgetSet(WidgetSet).MaskBlt(aHDC, x, aRect.top + TopPosition(aRect.bottom - aRect.top, bmp.Height), bmp.Width, bmp.Height, hdcMem, 0, 0, bmp.MaskHandle, 0, 0);
SelectObject(hdcMem, hbmpOld);
end;
procedure DrawMenuItem(const aMenuItem: TMenuItem; const aHDC: HDC; const aRect: Windows.RECT; const aSelected: boolean);
begin
if aMenuItem.IsLine then
DrawSeparator(aHDC, aRect)
else
begin
DrawMenuItemText(aMenuItem, aHDC, aRect, aSelected);
if aMenuItem.Checked then
DrawMenuItemCheckMark(aMenuItem, aHDC, aRect, aSelected);
if aMenuItem.hasIcon then
DrawMenuItemIcon(aMenuItem, aHDC, aRect, aSelected);
end;
end;
procedure TriggerFormUpdate(const AMenuItem: TMenuItem);
var
lMenu: TMenu;
begin
lMenu := AMenuItem.GetParentMenu;
if (lMenu<>nil) and (lMenu.Parent<>nil)
and (lMenu.Parent is TCustomForm)
and TCustomForm(lMenu.Parent).HandleAllocated
and not (csDestroying in lMenu.Parent.ComponentState) then
AddToChangedMenus(TCustomForm(lMenu.Parent).Handle);
end;
function ChangeMenuFlag(const AMenuItem: TMenuItem; Flag: Integer; Value: boolean): boolean;
var
MenuInfo: MENUITEMINFO;
begin
MenuInfo.cbSize := menuiteminfosize;
MenuInfo.fMask := MIIM_TYPE;
MenuInfo.dwTypeData := nil; // don't retrieve caption
GetMenuItemInfo(AMenuItem.Parent.Handle, AMenuItem.Command, false, @MenuInfo);
if Value then
MenuInfo.fType := MenuInfo.fType or Flag
else
MenuInfo.fType := MenuInfo.fType and (not Flag);
MenuInfo.dwTypeData := LPSTR(AMenuItem.Caption);
Result := SetMenuItemInfo(AMenuItem.Parent.Handle, AMenuItem.Command, false, @MenuInfo);
TriggerFormUpdate(AMenuItem);
end;
{ TWin32WSMenuItem }
procedure UpdateCaption(const AMenuItem: TMenuItem; ACaption: String);
var
MenuInfo: MENUITEMINFO;
begin
with MenuInfo do
begin
cbsize := menuiteminfosize;
if ACaption <> '-' then
begin
fType := MFT_STRING;
fMask:=MIIM_TYPE;
dwTypeData:=LPSTR(ACaption);
cch := StrLen(dwTypeData);
end
else fType := MFT_SEPARATOR;
end;
SetMenuItemInfo(AMenuItem.Parent.Handle, AMenuItem.Command, false, @MenuInfo);
with MenuInfo do
begin
cbsize := menuiteminfosize;
fMask := MIIM_TYPE;
fType := MFT_OWNERDRAW;
dwTypeData:=LPSTR(ACaption);
cch := StrLen(dwTypeData);
end;
SetMenuItemInfo(AMenuItem.Parent.Handle, AMenuItem.Command, false, @MenuInfo);
TriggerFormUpdate(AMenuItem);
end;
class procedure TWin32WSMenuItem.AttachMenu(const AMenuItem: TMenuItem);
var
MenuInfo: MENUITEMINFO;
ParentMenuHandle: HMenu;
ParentOfParent: HMenu;
begin
ParentMenuHandle := AMenuItem.Parent.Handle;
{Following part fixes the case when an item is added in runtime
but the parent item has not defined the submenu flag (hSubmenu=0) }
if AMenuItem.Parent.Parent<>nil then
begin
ParentOfParent := AMenuItem.Parent.Parent.Handle;
with MenuInfo do begin
cbSize := menuiteminfosize;
fMask:=MIIM_SUBMENU;
end;
GetMenuItemInfo(ParentOfParent, AMenuItem.Parent.Command,
false, @MenuInfo);
if MenuInfo.hSubmenu=0 then // the parent menu item is not yet defined with submenu flag
begin
MenuInfo.hSubmenu:=ParentMenuHandle;
SetMenuItemInfo(ParentOfParent, AMenuItem.Parent.Command,
false, @MenuInfo);
end;
end;
with MenuInfo do begin
cbsize := menuiteminfosize;
if AMenuItem.Enabled then fState:=MFS_ENABLED else fstate:=MFS_GRAYED;
if AMenuItem.Checked then fState:=fState or MFS_CHECKED;
fMask:=MIIM_ID or MIIM_DATA or MIIM_STATE or MIIM_TYPE;
wID:=AMenuItem.Command; {value may only be 16 bit wide!}
dwItemData:=PtrInt(AMenuItem);
if (AMenuItem.Count > 0) then
begin
fMask := fMask or MIIM_SUBMENU;
hSubMenu := AMenuItem.Handle;
end else
hSubMenu := 0;
if not AMenuItem.IsLine then
begin
fType:=MFT_OWNERDRAW;
end else begin
fType:=MFT_OWNERDRAW or MFT_SEPARATOR;
fState:=fState or MFS_DISABLED;
end;
dwTypeData := PChar(AMenuItem);
if AMenuItem.RadioItem then fType := fType or MFT_RADIOCHECK;
if (AMenuItem.GetIsRightToLeft) then
begin
fType := fType or MFT_RIGHTORDER;
//Reverse the RIGHTJUSTIFY to be left
if not AMenuItem.RightJustify then fType := fType or MFT_RIGHTJUSTIFY;
end
else if AMenuItem.RightJustify then fType := fType or MFT_RIGHTJUSTIFY;
end;
if dword(InsertMenuItem(ParentMenuHandle,
AMenuItem.Parent.VisibleIndexOf(AMenuItem), true, @MenuInfo)) = 0 then
DebugLn('InsertMenuItem failed with error: ', IntToStr(Windows.GetLastError));
TriggerFormUpdate(AMenuItem);
end;
class function TWin32WSMenuItem.CreateHandle(const AMenuItem: TMenuItem): HMENU;
begin
Result := CreatePopupMenu;
end;
class procedure TWin32WSMenuItem.DestroyHandle(const AMenuItem: TMenuItem);
begin
if Assigned(AMenuItem.Parent) then
DeleteMenu(AMenuItem.Parent.Handle, AMenuItem.Command, MF_BYCOMMAND);
DestroyMenu(AMenuItem.Handle);
TriggerFormUpdate(AMenuItem);
end;
class procedure TWin32WSMenuItem.SetCaption(const AMenuItem: TMenuItem; const ACaption: string);
begin
UpdateCaption(AMenuItem, aCaption);
end;
class function TWin32WSMenuItem.SetCheck(const AMenuItem: TMenuItem;
const Checked: boolean): boolean;
begin
UpdateCaption(AMenuItem, aMenuItem.Caption);
Result := Checked;
end;
class procedure TWin32WSMenuItem.SetShortCut(const AMenuItem: TMenuItem;
const OldShortCut, NewShortCut: TShortCut);
begin
UpdateCaption(AMenuItem, aMenuItem.Caption);
end;
class function TWin32WSMenuItem.SetEnable(const AMenuItem: TMenuItem; const Enabled: boolean): boolean;
var
EnableFlag: Integer;
begin
if Enabled then EnableFlag := MF_ENABLED
else EnableFlag := MF_GRAYED;
EnableFlag := EnableFlag or MF_BYCOMMAND;
Result := Boolean(Windows.EnableMenuItem(AMenuItem.Parent.Handle, AMenuItem.Command, EnableFlag));
TriggerFormUpdate(AMenuItem);
end;
class function TWin32WSMenuItem.SetRightJustify(const AMenuItem: TMenuItem; const Justified: boolean): boolean;
begin
Result := ChangeMenuFlag(AMenuItem, MFT_RIGHTJUSTIFY, Justified);
end;
{ TWin32WSMenu }
class function TWin32WSMenu.CreateHandle(const AMenu: TMenu): HMENU;
begin
Result := CreateMenu;
end;
class procedure TWin32WSMenu.BiDiModeChanged(const AMenu: TMenu);
begin
if AMenu.HandleAllocated then
begin
SetMenuFlag(AMenu.Handle, MFT_RIGHTORDER or MFT_RIGHTJUSTIFY, AMenu.IsRightToLeft);
//TriggerFormUpdate not take TMenu, we repeate the code
if (AMenu<>nil) and (AMenu.Parent<>nil)
and (AMenu.Parent is TCustomForm)
and TCustomForm(AMenu.Parent).HandleAllocated
and not (csDestroying in AMenu.Parent.ComponentState) then
AddToChangedMenus(TCustomForm(AMenu.Parent).Handle);
end;
end;
{ TWin32WSPopupMenu }
class function TWin32WSPopupMenu.CreateHandle(const AMenu: TMenu): HMENU;
begin
Result := CreatePopupMenu;
end;
class procedure TWin32WSPopupMenu.Popup(const APopupMenu: TPopupMenu; const X, Y: integer);
var
MenuHandle: HMENU;
AppHandle: HWND;
const
lAlign: array[Boolean] of Word = (TPM_LEFTALIGN, TPM_RIGHTALIGN);
begin
MenuHandle := APopupMenu.Handle;
AppHandle := TWin32WidgetSet(WidgetSet).AppHandle;
GetWindowInfo(AppHandle)^.PopupMenu := APopupMenu;
TrackPopupMenuEx(MenuHandle, lAlign[APopupMenu.IsRightToLeft] or TPM_LEFTBUTTON or TPM_RIGHTBUTTON,
X, Y, AppHandle, Nil);
end;
initialization
if (Win32MajorVersion = 4) and (Win32MinorVersion = 0) then
menuiteminfosize := W95_MENUITEMINFO_SIZE
else
menuiteminfosize := sizeof(TMenuItemInfo);
////////////////////////////////////////////////////
// I M P O R T A N T
////////////////////////////////////////////////////
// To improve speed, register only classes
// which actually implement something
////////////////////////////////////////////////////
RegisterWSComponent(TMenuItem, TWin32WSMenuItem);
RegisterWSComponent(TMenu, TWin32WSMenu);
// RegisterWSComponent(TMainMenu, TWin32WSMainMenu);
RegisterWSComponent(TPopupMenu, TWin32WSPopupMenu);
////////////////////////////////////////////////////
end.