unit UnitFrmDummyUnicodePopup;
{
    Purpose:
        Wrap all the logic needed to to create a low level API popup
        menu that has Unicode text and gather the data needed
        to show a Unicode Tooltip hint (see FrmDummyUnicodeTooltip

    Updates:

        Code completely re-written and simplified - FrmMainPopup's popup
        menu it traversed and duplicated instead of creating it from scratch
        
        -------------

        Show tooltip for "All Items..." hover
        ------------------------
        "All Items..." support
        ------------------------

        "Number of characters to show for each item" required a restart to
        work with the unicode popup

        ----------------------
        Let Permanent Items grow as large as they want
        "Beep" on accelerator key

}

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StrUtils, INIFIles, Math, Menus,

  UnitMisc, UnitClipQueue, UnitFrmMainPopup, UnitTWideChar, 
  UnitOtherQueue, UnitKeyboardQuery, UnitFrmDummyUnicodeTooltip, Contnrs,
  StdCtrls,
  UnitFrmTooltip, UnitClipMenu;

type
  TFrmDummyUnicodePopup = class(TForm)
    timShow: TTimer;
    Label1: TLabel;
    timDelayedCommand: TTimer;
    TimClose: TTimer;
    TimAutoGrow: TTimer;
    timShowClipMenu: TTimer;
    timDelayedSpace: TTimer;
    procedure timShowTimer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure timDelayedCommandTimer(Sender: TObject);
    procedure TimCloseTimer(Sender: TObject);
    procedure TimAutoGrowTimer(Sender: TObject);
    procedure timShowClipMenuTimer(Sender: TObject);
    procedure timDelayedSpaceTimer(Sender: TObject);
  private
    { Private declarations }
    LastCursorPos : TPoint;
    LastIdlePos : TPoint;
    LastHint : string;
    LastIsMenuItem : boolean;
    LastIsFullConfigureItem : boolean;
    LastClipItem : TClipItem;
    LastMenuItem : TMenuItem;
    LastSelectID : integer;
    LastMenuRect : Trect;
    LastHotkeyName : string;
    LastInApplicationIcon : boolean;
    LastIsPermItem : boolean;
    LastPermGroup, LastPermIndex : integer;
    CurrentClipboard : TClipItem;

    KeyToItemID : THashedStringList;
    //VisibleCharCount : integer;
    //Tooltip : TTooltipWindow;
    Tooltip : TTooltipWindow;
    PasteTooltip : TTooltipWindow;
    TimeFiring : boolean;

    SubMenuItemList : THashedStringList;
    SubObjectList : TObjectList;

    mi : TMenuITem;
    m : TWMCommand;

    delayCommand : boolean;

    InClipTypeIcon : boolean;
    InApplicationIcon : boolean;

    RightClicked : boolean;
    TabCommand : boolean;

    pop : HMENU;
    popupmenu : TPopupMenu;
    CurrentClipboardMenuID : integer;

    LastToolTipGrown : boolean;
    fim : TClipMenu;

    MouseHighlighted : boolean;
    PopupInited : boolean;
    fmods, fkey : word;
    WinkeyDetected : boolean;
    HotkeyCharDetected : boolean;
    FirstEnterIdle : boolean;

    LastPoint : TPoint;

    function ItemIDToMenuPosition(itemID : cardinal) : integer;
    function ItemIDToMenuItem(itemID : cardinal) : TMenuItem;
    function MouseInCliptypeIcon : boolean;
    function MouseInApplicationIcon : boolean;
    procedure DisplayTootlip;
    procedure ClearItemMenu;
    procedure ItemMenuCreate;
    procedure ItemMenuPopupClipMode;
    procedure ItemMenuPermanentClipMode;
    procedure ItemMenuDelayedClose;
    procedure IMEditClick(Sender : TObject);
    procedure IMDeleteClick(Sender : Tobject);
    procedure IMDestroyClick(Sender : Tobject);
    procedure IMPasteClick(Sender : Tobject);
    procedure IMPermanentClick(Sender : Tobject);
  public
    { Public declarations }

    procedure ReportHokey(mods, key : word);
    procedure PopulatePopupUnicode(var M : TMenuItem; parent : HMENU);
    procedure ShowPopup(x,y : integer; inpopup : TPopupMenu);
    function GetLastPoint : TPoint;
    function PopupIsInited : boolean;


    procedure WMDrawItem(var Msg: TWMDrawItem); message WM_DRAWITEM;
    procedure WMMeasureItem(var Msg: TWMMeasureItem); message WM_MEASUREITEM;
    procedure WMCommand(var Msg : TWMCommand); message WM_COMMAND;
    procedure WMMenuChar(var Msg : TWMMenuChar); message WM_MENUCHAR;
    procedure WMMenuSelect(var Msg : TWMMenuSelect); message WM_MENUSELECT;
    procedure WMEnterIdle(var Msg : TWMEnterIdle); message WM_ENTERIDLE;

    procedure WMInitMenuPopup(var Msg: TWMInitMenuPopup); message WM_INITMENUPOPUP;
  end;


    
var
  FrmDummyUnicodePopup: TFrmDummyUnicodePopup;
{////////////////////}
{//}implementation{//}
{////////////////////}
{$R *.dfm}


uses UnitFrmPermanentNew, UnitFrmConfig, UnitFrmSysTrayMenu, UnitPaste,
  Types, UnitMenuItemTagdata, UnitFrmEditItem, UnitPopupGenerate, GraphUtil {for color routines},
  UnitFrmEditTextExternal;
const //PU_LAST_TEXT = 'Las&t: ';
PU_LAST_TEXT = 'Last: ';
      //PU_CURRENT_TEXT = 'Cu&rrent: ';
PU_CURRENT_TEXT = 'Current: ';


CONST LEFT_COLUMN_WIDTH = 22;
CONST ICON_SPACER = LEFT_COLUMN_WIDTH + 4;

procedure TFrmDummyUnicodePopup.ClearItemMenu;
begin
    //UnitMisc.AppendLog('Hiding Item Menu');
    fim.Hide;
    timShowClipMenu.Enabled := false;
end;

procedure TFrmDummyUnicodePopup.DisplayTootlip;
var ci :TClipItem;
    pos :    TPoint;
begin
    pos := mouse.CursorPos;

    if not Windows.PtInRect(self.LastMenuRect, mouse.CursorPos) then begin
        pos.X := self.LastMenuRect.left + ((self.LastMenuRect.right - self.lastmenurect.Left)div 2) ;
        pos.Y := self.LastMenuRect.bottom;
    end;

    pos.y := self.LastMenuRect.Bottom;
    inc(pos.x, 30);
    inc(pos.y, 0);
    UnitMisc.AppendLog('Showing Tooltip hint');

    InClipTypeIcon := false;
    LastInApplicationIcon := InApplicationIcon;
    InApplicationIcon := false;


    if self.MouseInCliptypeIcon then begin
        Tooltip.CloseTooltip;
        PasteToolTip.ShowTooltip(
            'Press TAB or Click here to paste as ' + self.LastClipItem.GetFormatName,
           pos, false );
        InClipTypeIcon := true;
    end else if self.MouseInApplicationIcon  then begin
        Tooltip.CloseTooltip;
        //PasteTooltip.ShowTooltip('Click here to Edit', pos, false);

        InApplicationIcon := true;
    end else if (self.LastClipItem <> nil) then begin
        PasteTooltip.CloseTooltip;
        Tooltip.ShowTooltip(self.LastClipITem, pos,self.LastHotkeyName);
    end else begin
        PasteTooltip.CloseTooltip;

        if (FrmPermanent.IsComplexItem(self.LastHint)) then begin
            ci := FrmPermanent.GetComplexItem(self.LastHint);
            Tooltip.ShowTooltip(ci, pos, self.LastHotkeyName );
            MyFree(ci);
        end else begin
            if self.LastHint <> '' then
                Tooltip.ShowTooltip(self.LastHint, pos, true, self.LastHotkeyName );
        end;
    end;
end;

procedure TFrmDummyUnicodePopup.FormCreate(Sender: TObject);
begin
    UnitMisc.AppendLog('FrmDummyUnicodePopup - creating');

    KeyToItemID := THashedStringList.Create;
    CurrentClipboard := TClipItem.Create;

    LastClipItem := nil;
    
    Tooltip := TTooltipWindow.Create;;
    PasteTooltip := TTooltipWindow.Create;

    self.SubMenuItemList := THashedStringList.Create;
    SubObjectList := TObjectList.Create;
    SubObjectList.OwnsObjects := false;


    mi := TMenuItem.Create(self);

    self.ItemMenuCreate;
end;

procedure TFrmDummyUnicodePopup.FormDestroy(Sender: TObject);
begin

    SubMenuItemList.Free;
    SubObjectList.Free;


    mi.Free;
    timShow.Enabled := false;
    timDelayedCommand.Enabled := false;
    TimAutoGrow.Enabled := false;
end;



function TFrmDummyUnicodePopup.GetLastPoint: TPoint;
begin
    result := self.LastMenuRect.TopLeft;
end;

procedure TFrmDummyUnicodePopup.WMInitMenuPopup(var Msg: TWMInitMenuPopup);
var c : cardinal;
    mi : MENUITEMINFO;
    m : TMenuItem;
begin
    inherited;
    if msg.MenuPopup = pop then begin
        self.FirstEnterIdle := true;


        if FrmConfig.cbHighlightMove.Checked  then begin
            Paste.SendDOWNSpecial;
        end;


        appendlog('!!!!Current MenuID'+inttostr(CurrentClipboardMenuID));

        if CurrentClipboardMenuID <> -1 then begin


            // Hilite will only draw the hilite state, arrow keys break it and cause 2 hilites
            //HiliteMenuItem(self.Handle, pop, CurrentClipboardMenuID, MF_HILITE or MF_BYCOMMAND);
            //HiliteMenuItem(self.Handle, pop, CurrentClipboardMenuID, MF_HILITE or MF_BYCOMMAND);

            // ditto
            {m := self.ItemIDToMenuItem(CurrentClipboardMenuID);
            Windows.GetMenuItemInfo(pop, CurrentClipboardMenuID,longbool(false), mi);
            mi.fState := mi.fState or MFS_HILITE or MFS_DEFAULT;
            Windows.SetMenuItemInfo(pop, CurrentClipboardMenuID,longbool(false), mi);}

        end;
    end;
end;



procedure TFrmDummyUnicodePopup.ShowPopup(x, y: integer;  inpopup: TPopupMenu);
var //p : HMENU;
    m : TMenuItem;
    r : TRect;
    pt : TPoint;
begin
    Windows.SetLastError(ERROR_SUCCESS);
    //self.VisibleCharCount := FrmConfig.udCharacters.Position;
    self.RightClicked := false;
    self.LastMenuRect := rect(0,0,0,0);

    self.LastIsMenuItem := false;
    self.LastIsFullConfigureItem := false;
    self.LastIsPermItem := false;
    self.WinkeyDetected := false;
    self.HotkeyCharDetected := false;
    
    self.popupmenu := inpopup;

    FrmMainPopup.SetItemMenuOverride(false);
    InApplicationIcon := false;
    
    UnitMisc.AppendLog('translate');
    pop := Windows.CreatePopupMenu;
    m := inpopup.items;

    UnitMisc.AppendLog('current');
    self.CurrentClipboard.Free;
    self.CurrentClipboard := TClipItem.Create;
    self.CurrentClipboard.GetClipboardItem(0);
    
    self.fim.free;
    self.fim := nil;
    self.ItemMenuCreate;


    self.PopulatePopupUnicode(m, pop);
    self.PopupInited := false;
    UnitMisc.AppendLog('track');
    // show it
    Windows.TrackPopupMenu(pop,
        TPM_LEFTBUTTON or TPM_RIGHTBUTTON or TPM_LEFTALIGN,
        x, y, 0,
        self.Handle, nil);

    GetMenuItemRect(self.Handle, pop,0,r);

    LastPoint:= self.ClientToScreen(r.TopLeft); 

    UnitMisc.AppendLog('draw');
    Windows.DrawMenuBar(pop);
    self.PopupInited := false;

    if fim.Visible and Windows.PtInRect(fim.BoundsRect, mouse.CursorPos) then begin
        FrmMainPopup.SkipFocusReturnOnce;
    end else begin
        self.ClearItemMenu;
    end;

    UnitMisc.AppendLog('cleanup');

    // clean up
    while Windows.GetMenuItemCount(pop) > 0 do begin
        Windows.DeleteMenu(pop,0, MF_BYPOSITION);
    end;


    Tooltip.CloseTooltip;
    PasteTooltip.CloseTooltip;

    timShow.Enabled := false;
    timclose.Enabled := false;
    TimAutoGrow.Enabled := false;



    self.ItemMenuDelayedClose;
end;

// Call Order
//--------------
// Populate -> Measure Item -> Draw Item
// Some user event trigers
// (MenuSelect, EnterIdle, Command)

procedure TFrmDummyUnicodePopup.PopulatePopupUnicode(var M : TMenuItem; parent : HMENU);
    procedure TraverseMenuItems(var m : TMenuItem; parent : HMENU; level : integer = 0) ;
        procedure MyAppend(hm: HMenu; c: cardinal; id : Cardinal; pc : PChar);
        begin
            if m.break <> mbNone then
                c := c or MF_MENUBARBREAK;
            Windows.AppendMenu(hm, c, id, pc);
        end;
    var i : integer;
        menu : HMENU;
        m2 : TMenuItem;
        s : string;
        ni, ni2 : TNotifyEvent;
        flags : cardinal;
        mitd : TMenuItemTagData;
    begin
        if (m.count = 0) then begin
            if (m.IsLine) then begin


                i := SubObjectList.Add(m);
                MyAppend(parent,MF_OWNERDRAW or MF_SEPARATOR or MF_BYCOMMAND, i, nil);
            end else begin
                i := SubObjectList.Add(m);

                mitd := TagDataList.Get(m.tag);

                if (mitd.clip <> nil) and (mitd.clip.CData.Hash = self.CurrentClipboard.CData.Hash)  then begin
                    CurrentClipboardMenuID := i;
                end;


                ni := m.OnClick;
                ni2 := frmSysTrayMenu.MethodMenuItemClickEvent;
                if (m.Checked) then begin
                    // checked items are not custom drawn
                    s := m.Caption;
                    flags :=MF_ENABLED or MF_BYCOMMAND or MF_CHECKED or MF_OWNERDRAW;
                    MyAppend(parent,flags,
                        i, PChar(s));
                end else begin
                    flags := MF_OWNERDRAW;
                    MyAppend(parent, flags, i, nil);
                end;
            end;
        end else begin
            menu := 0;
            if (level <> 0) then begin
                menu := Windows.CreatePopupMenu;
                //UnitMisc.MySetTheme(menu);
            end;
            for i := 0 to (m.Count - 1) do begin
                m2 := m.Items[i];
                if (level <> 0) then begin
                    TraverseMenuItems(m2, menu, level + 1);
                end else begin
                    TraverseMenuItems(m2, parent, level + 1);
                end;
            end;
            if (level <> 0) then begin
                // the HMENU value is as the ID for drawing
                // instead of any user submitted number for submenu items (MF_POPUP)
                i := SubObjectList.Add(m);
                SubMenuItemList.Values[IntToStr(menu)] := IntToStr(i);
                flags := MF_OWNERDRAW or MF_POPUP;
                MyAppend(parent, flags, menu, PChar(-1));
            end;
        end;
    end;
begin
    SubObjectList.Clear;
    SubMenuItemList.clear;
    CurrentClipboardMenuID := -1;    
    TraverseMenuItems(m, parent, 0);

  

end;


function TFrmDummyUnicodePopup.PopupIsInited: boolean;
begin
    result := self.PopupInited;
end;

procedure TFrmDummyUnicodePopup.ReportHokey(mods, key: word);
begin
    fmods := mods;
    fkey := key;

    Paste.SendDOWNSpecial;
    self.WinkeyDetected := false;
    HotkeyCharDetected := true;
end;

{
//
// These 2 messages are just do draw the damn popu
//
}

procedure TFrmDummyUnicodePopup.WMMeasureItem(var Msg: TWMMeasureItem);
var id : Cardinal;
    s : string;
    sz : TSize;

    procedure GetTextSizeOf( s : string );
    begin
        Windows.GetTextExtentPoint32(self.Canvas.Handle, PChar(s),
        min(length(s), 200),
        sz);
        msg.MeasureItemStruct^.itemWidth := max(sz.cx + loword(GetMenuCheckMarkDimensions),20);
        msg.MeasureItemStruct^.itemHeight := max(sz.cy+1, 6);
    end;
    procedure GetTextSizeOfNoLimit( s : string );
    begin
        Windows.GetTextExtentPoint32(self.Canvas.Handle, PChar(s),
        length(s),
        sz);
        msg.MeasureItemStruct^.itemWidth := max(sz.cx + loword(GetMenuCheckMarkDimensions),20);
        msg.MeasureItemStruct^.itemHeight := max(sz.cy+1, 6);
    end;

var
    mi2 : TMenuItem;
    mitd : TMenuItemTagData;
begin
    //UnitMisc.AppendLog('wmmeasure');

    Windows.SetLastError(ERROR_SUCCESS);
    id := msg.MeasureItemStruct^.itemID;

    //
    // Basically, use the longest text item to gauge the width of the popup
    // At a minimum the height is the 2 pixels above the size of an icon
    //


    mi2 := self.ItemIDToMenuItem(id);
    s := '';
    if mi2 <> nil then begin
        s := mi2.Caption;
    end;



    msg.MeasureItemStruct^.itemWidth := 100;
    if (s <> '') then begin
        GetTextSizeOf(s);
    end else begin
        s := self.CurrentClipboard.GetAsText;
        GetTextSizeOf(PU_LAST_TEXT + s);
    end;

    try
        if (mi2 <> nil) and (mi2.tag <> 0) then begin

            mitd := TagDataList.Get(mi2.tag);
            case mitd.itemtype of
            IT_POPUP:
                begin
                    inc(msg.MeasureItemStruct^.itemWidth, loword(GetMenuCheckMarkDimensions));
                    msg.MeasureItemStruct^.itemHeight := msg.MeasureItemStruct^.itemHeight;
                end;
            IT_MENU,  IT_TEMP,IT_CLICKED, IT_OTHER, IT_CLIPBOARD, IT_SELECTED, IT_LAST, IT_PROGRAM, IT_SYSTEM:
                begin
                    // account for ICON height
                    //msg.MeasureItemStruct^.itemHeight := max(msg.MeasureItemStruct^.itemHeight, 16 + 2);
                    msg.MeasureItemStruct^.itemHeight := msg.MeasureItemStruct^.itemHeight+1;

                    // menu item was too small [because of accelerator showing?]
                    if mitd.itemtype = IT_MENU then begin
                        msg.MeasureItemStruct^.itemWidth :=  msg.MeasureItemStruct^.itemWidth + 20;
                    end;

                    // small fonts can cause icon drawing problems
                end;
            IT_PERMANENT:
                    msg.MeasureItemStruct^.itemHeight := msg.MeasureItemStruct^.itemHeight;

            end;
            if (mitd.icon <> 0) or (mi2.Bitmap.Height <> 0) then begin
                msg.MeasureItemStruct^.itemHeight := msg.MeasureItemStruct^.itemHeight;
            end;
        end;
    except
    end;

    // ensure at least 100 pixels, but limit to Width Max setting
    msg.MeasureItemStruct^.itemWidth := max(msg.MeasureItemStruct^.itemWidth + LEFT_COLUMN_WIDTH, 100);
    msg.MeasureItemStruct^.itemWidth := min(frmconfig.UDWidth.Position, msg.MeasureItemStruct^.itemWidth);

    msg.MeasureItemStruct^.itemHeight := max(frmconfig.udMinHeight.Position,msg.MeasureItemStruct^.itemHeight);
    {if LastSelectID = msg.measureitemstruct^.itemID then begin
        inc(msg.MeasureItemStruct^.itemHeight,4);
    end;}

end;


procedure TFrmDummyUnicodePopup.WMDrawItem(var Msg: TWMDrawItem);
var
    tc, bc : cardinal;
    dc : integer;
    TruncateCaption : boolean;
    selected : boolean;
    IsCurrentMenuItem : boolean;

    function BitmapToIcon(bm : TBitmap) : TIcon;
    var
        il : TImageList;
    begin
        result := nil;
        if (bm <> nil) and ((bm.Width and bm.Height) <> 0) then begin
            result := TIcon.Create;
            il := TImageList.CreateSize(bm.Width, bm.Height);
            il.AddMasked(bm, bm.TransparentColor);
            il.GetIcon(0, result);
            il.Free;
        end;
    end;

    //
    // Since it's owner drawns, I have to know what &x character related
    // to what ItemID
    //
    procedure UpdateAccessKeys(s : string);
    var amp : integer;
    begin
        amp := pos('&', s);
        if (amp <> 0) then begin
            inc(amp);
            KeyToItemID.Values[lowercase(s[amp])] := IntToStr(msg.DrawItemStruct^.itemID);
        end;
    end;

    function GetTextOptions(s : string) : integer;
    const MY_TEXT_OPTIONS = DT_VCENTER or DT_SINGLELINE or
        DT_MODIFYSTRING or DT_END_ELLIPSIS or DT_NOPREFIX;
    begin
        result := DT_VCENTER or DT_SINGLELINE;

        if TruncateCaption then begin
            result := result or DT_MODIFYSTRING {or DT_END_ELLIPSIS};
            if (pos('://',s) in [1..7])  then result := result or DT_PATH_ELLIPSIS and not DT_END_ELLIPSIS;
            if (pos(':\',s) in [1..7]) then result := result or DT_PATH_ELLIPSIS and not DT_END_ELLIPSIS;
        end;
    end;

    procedure DrawBitmap(hdc : HDC; bm : TBitmap; top, left, width, height : integer);
    var ico : TIcon;
        il : TImageList;
    begin
        if (bm <> nil) and ((bm.Width and bm.Height) <> 0) then begin
            ico := TIcon.Create;
            il := TImageList.CreateSize(bm.Width, bm.Height);
            il.AddMasked(bm, bm.TransparentColor);
            il.GetIcon(0, ico);
            il.Free;
            if (ico.Handle <> 0) then begin
                DrawIconEx(hdc,
                        left,
                        top,
                        ico.Handle,
                        width,height, 0, 0, DI_NORMAL);
            end;
            ico.Free;
        end;
    end;

    procedure DrawTextAndBitmap(bm : TBitmap; s : string);
    var r : TRect;
    begin
        with msg.DrawItemStruct^ do begin
            UpdateAccessKeys(s);

            r.Top := rcItem.Top;
            r.Bottom := rcItem.Bottom;
            r.Left := rcItem.Left + ICON_SPACER;
            r.Right := rcItem.Right; //- (ICON_SPACER - 4);

            DrawText(hdc, PChar(s), length(s),
                r, GetTextOptions(s) and not DT_MODIFYSTRING );

            if bm = nil then EXIT;

            DrawBitmap(hdc, bm, rcItem.Top, rcItem.Left, rcitem.bottom-rcitem.top-1, rcitem.bottom-rcitem.top-1);
        end;
    end;

    procedure DropShadowHorizontal(hdc : HDC; percent : double; y, x1, x2 : integer; OverrideColor : cardinal = 0 );
    var i : integer;
        c : cardinal;
        r, b, g : byte;

    begin
        for i := x1 to x2 do begin
            c := GetPixel(HDC,i, y);
            if OverrideColor <> 0 then c := OverrideColor;

            r := (c and $FF0000) shr 16;
            g := (c and   $FF00) shr 8;
            b := (c and     $FF);

            r := min(255, trunc(r * percent));
            g := min(255, trunc(g * percent));
            b := min(255, trunc(b * percent));

            c := RGB(r,g,b);
            SetPixel(HDC, i,y, c);
        end;
    end;
    procedure DropShadowVertical(hdc : HDC; percent : double; x, y1, y2 : integer; OverrideColor : cardinal = 0 );
    var i  : integer;
        c : cardinal;
        r, b, g : byte;
    begin
        for i := y1 to y2 do begin
            c := GetPixel(HDC, x,i);
            if OverrideColor <> 0 then c := OverrideColor;

            r := GetRValue(c);
            g := GetGValue(c);
            b := GetBValue(c);

            r := min(255, trunc(r * percent));
            g := min(255, trunc(g * percent));
            b := min(255, trunc(b * percent));

            c := RGB(r,g,b);
            SetPixel(HDC, x,i, c);
        end;
    end;

    {procedure DropShadowBox(hdc : HDC; percent : double; x1,x2, y1,y2 : integer);
    var i,j : integer;
        c : cardinal;
        r,g,b : byte;
    begin
        for i := x2 downto x1 do begin
            for j := y2 downto y1 do begin
                c := GetPixel(HDC, i,j);
                if (c = 0) and (GetPixel(HDC, i+1, j+1) = $FFFFFF) then begin
                    //r := (c and $FF0000) shr 16;
                    //g := (c and   $FF00) shr 8;
                    //b := (c and     $FF);

                    r := min(255, trunc(255 * percent));
                    g := min(255, trunc(255 * percent));
                    b := min(255, trunc(255 * percent));

                    c := RGB(r,g,b);

                    SetPixel(HDC, i+1,j+1, c);
                end;

            end;
        end;

    end;}

    procedure ClipTypeMark(hdc : HDC; rcItem : trect; cliptypeicon : tbitmap; oppacity : double = 1.0);
    var i : integer;
        top, left, width, height : integer;
        c : cardinal;
    begin
        if cliptypeicon = nil then EXIT;

        left :=  rcItem.Right   - cliptypeicon.Width - 3;
        top := rcItem.top + 1;
        width := cliptypeicon.Width;
        height := cliptypeicon.Height;

        cliptypeicon.TransparentColor := $AAAAAA; // unused color
        DrawBitmap(hdc, cliptypeicon, top, left, width, height);

        c := 0;
        DropShadowVertical(hdc, 0.9, left, top, top+height,c);
        DropShadowHorizontal(hdc,1.3,top, left,left+width,c);

        DropShadowVertical(hdc, 0.8, left+width-1, top+2, top+height,c);
        DropShadowHorizontal(hdc,0.8,top+height-1,left+2,left+width,c);
        if selected then begin
            c := ColorToRGB(clHighLightText);
        end;
        DropShadowVertical(hdc, 0.8, left+width, top+2, top+height,c);
        DropShadowHorizontal(hdc,0.8,top+height,left+2,left+width,c);


        if (oppacity < 1.0) then begin
            for i := top to top+height do begin
                DropShadowHorizontal(hdc, oppacity, i, left, left + width);
            end;
        end;
    end;

    {procedure DrawUnicodeAndIcons(programicon : HICON; cliptypeicon : TBitmap; ci : TClipItem; prefix : string);
    var wc : TWideChar;
        r : TRect;
        sz : TSize;
        len : integer;
    begin
        UpdateAccessKeys(prefix);

        wc := TWideChar.Create;
        wc.Append(prefix);
        wc.Append(ci.GetHandle, ci.GetDataSize);

        //wc.Replace(WideChar($9), WideChar($3)); // replace these whitespace
        //wc.Replace(WideChar(#13),WideChar($3)); // characters with "squares"
        //wc.Replace(WideChar(#10),WideChar($3)); //


        len := min(100, wc.StrLength -1);

        Windows.GetTextExtentPoint32W(self.Canvas.Handle, wc.Memory,
            wc.StrLength,sz);


        with msg.DrawItemStruct^ do begin
            r.Top := rcItem.Top + 1;
            r.Bottom := r.top + sz.cy;
            r.Left := rcItem.Left + ICON_SPACER;
            r.Right := rcItem.Right;
            dec(r.Right, ICON_SPACER);

            // trim the rectangle so the next line of text won't be
            // partially visible

            DrawTextW(hdc, PWideChar(wc.Memory), len,
                r, GetTextOptions(ci.GetAsText) );

            wc.Clear;
            MyFree(wc);

            if (programicon <> 0) then begin
                DrawIconEx(hdc,
                    rcItem.left, rcItem.top,
                    programicon,
                    16,16, 0, 0, DI_NORMAL);
            end;
        end;
    end;
    }

    {procedure DrawTextAndIcons(h : HICON; cliptypeicon : TBitmap; s : string); overload;
    var r : TRect;
    begin
        UpdateAccessKeys(s);

        with msg.DrawItemStruct^ do begin
        r.Top := rcItem.Top;
        r.Bottom := rcItem.Bottom + 1;
        r.Left := rcItem.Left + ICON_SPACER;
        r.Right := rcItem.Right - ICON_SPACER;

        DrawText(hdc, PChar(s), length(s),
            r, GetTextOptions(s));

        if (h <> 0) then begin
            DrawIconEx(hdc,
                    rcItem.left, rcItem.top,
                    h, 16,16,
                    0, 0, DI_NORMAL);
        end;


        ClipTypeMark(hdc, rcItem, cliptypeicon);
        end;
    end;
    }
    {procedure DrawTextAndIcon(h : HICON; s : string); overload;
    var r : TRect;
    begin
        UpdateAccessKeys(s);

        with msg.DrawItemStruct^ do begin
        r.Top := rcItem.Top;
        r.Bottom := rcItem.Bottom;
        r.Left := rcItem.Left + ICON_SPACER;
        r.Right := rcItem.Right;

        DrawText(hdc, PChar(s), length(s),
            r, GetTextOptions(s) );

        if (h <> 0) then begin
            DrawIconEx(hdc,
                    rcItem.left, rcItem.top,
                    h, 16,16,
                    0, 0, DI_NORMAL);
        end;
        end;
    end;}

    function ClipTypeBitmap(ci : TClipItem) : TBitmap;
        function bm(img : TImage) : TBitmap;
        begin
            result := img.Picture.Bitmap;
        end;
    begin
        result := bm(FrmMainPopup.imgUnkown);
        case ci.GetFormat of
        CF_TEXT : result := bm(FrmMainPopup.imgPlain);
        CF_UNICODETEXT : result := bm(FrmMainPopup.ImgUnicode);
        CF_HDROP : result := bm(FrmMainPopup.imgFile);
        CF_DIB : result := bm(FrmMainPopup.imgPic);
        end;

        if ci.GetFormat = UnitMisc.GetCF_HTML then begin
            result := bm(FrmMainPopup.imgHTML);
        end else if ci.GetFormat = UnitMisc.GetCF_RICHTEXT then begin
            result := bm(FrmMainPopup.imgRichText);
        end;
    end;


    procedure DrawMenuItem(prefix : string; ci : TClipItem; icon : HICON = 0; instead : string = ''; noiconbutton : boolean = true); overload;
    var h : HICON;
        r, rPrefix, button : TRect;
        s : string;
        wc : TWideChar;
        sz : tagSIZE;
        oppacity : double;
        len : integer;
        br : HBRUSH;
    begin
        with msg.DrawItemStruct^ do begin
            h := 0;
            if icon <> 0 then begin
                h := icon;
            end else if (ci <> nil) and (icon <> -1) then begin
                h := ci.CData.GetHICON;
            end;

            br := 0;
            if (IsCurrentMenuItem and not selected) then begin
                br := Windows.CreateSolidBrush( ColorToRGB(cl3DLight));
                Windows.SetBkColor(hdc, ColorToRGB(cl3DLight));
                FillRect(hdc, rcItem, br);
                Windows.DeleteObject(br);
            end;
            

            if (h <> 0)  then begin
                button.top := rcitem.top;
                button.bottom := rcitem.Bottom;
                //button.Left := 0;
                button.left := rcItem.Left;
                button.Right := button.left+ LEFT_COLUMN_WIDTH;

                if selected then begin
                    if (not noiconbutton) then begin
                        br := Windows.CreateSolidBrush(ColorToRGB(clBtnFace));
                        FillRect(hdc, button, br);
                        Windows.DeleteObject(br);
                        br := 0;
                    end;
                    {
                    DrawIconEx(hdc,
                        rcItem.left+2, rcItem.top+1,
                        h, 16,16,
                        0, 0, DI_NORMAL
                    );
                    }
                    DrawIconEx(hdc,
                        rcItem.left+2, rcItem.top+1,
                        h, rcitem.bottom-rcitem.top-1,rcitem.bottom-rcitem.top-2,
                        0, 0, DI_NORMAL
                    );

                    if (not noiconbutton) then begin
                        button.right := rcItem.left + 18+3;
                        Windows.DrawEdge(hdc,button,BDR_RAISEDinner, BF_TOP);
                        Windows.DrawEdge(hdc,button,BDR_RAISEDouter, BF_BOTTOM);
                        Windows.DrawEdge(hdc,button,BDR_RAISEDinner, BF_LEFT);
                        Windows.DrawEdge(hdc,button,BDR_RAISEDouter, BF_RIGHT);
                    end;
                end else begin

                    {DrawIconEx(hdc,
                        rcItem.left+2, rcItem.top+1,
                        h, 16,16,
                        0, 0, DI_NORMAL
                    );}
                    DrawIconEx(hdc,
                        rcItem.left+2, rcItem.top+1,
                        h, rcitem.bottom-rcitem.top-1,rcitem.bottom-rcitem.top-1,
                        0, 0, DI_NORMAL
                    );

                end;


                if IsCurrentMenuItem then begin
                    dec(button.Right,2); 
                    Windows.DrawFocusRect(hdc, button);
                end;

            end;

            rprefix := rect(0,0,0,0);
            if prefix <> '' then begin
                UpdateAccessKeys(prefix);


                s := prefix + '    ';
                DrawText(HDC, pchar(s), length(s), rPrefix, GetTextOptions(prefix) or DT_CALCRECT);
                s := trim(s) + ' ';
                DrawText(HDC, pchar(s), length(s), rPrefix, GetTextOptions(prefix) or DT_CALCRECT);

                rPrefix.Left := rcItem.left + ICON_SPACER + rPrefix.left ;
                rprefix.right := rcItem.Left + rPrefix.Right + ICON_SPACER;
                rPrefix.Top := rcItem.Top;
                rPrefix.Bottom := rcItem.Bottom;

                DrawText(hdc, PChar(s), length(s), rPrefix, GetTextOptions(prefix));
            end;
            r.top := rcItem.Top;
            r.bottom := rcItem.Bottom;
            r.Right := rcItem.Right - ICON_SPACER;
            r.left := rcItem.Left + ICON_SPACER + (rPrefix.right - rPrefix.left);
            if (instead = '')  and (ci <> nil) and (ci.GetFormat = CF_UNICODETEXT) then begin

                wc := TWideChar.Create;

                wc.Append(ci.GetHandle, ci.getdatasize);
                wc.Append(#0);
                len := min(100, wc.StrLength -2);
                sz.cx := 0;
                sz.cy := 0;
                DrawTextW(hdc, PWideChar(wc.Memory), len,
                    r, GetTextOptions(ci.GetAsText) );

                myfree(wc);
            end else begin
                s := '';
                if (ci = nil) then begin
                    if instead <> '' then
                        s := copy(instead,1,length(instead));
                end else begin
                    s := copy(ci.GetAsText,1,length(ci.GetAsText));
                end;
                // very strange - looks as if the compiler gives the address
                // of the object's string if "s := ci.GetAsText" - and the
                // data is altered
                if s <> '' then begin
                    len := min(100, length(s));
                    if (IsCurrentMenuItem) then begin
                        Windows.SetDCBrushColor(hdc, ColorToRGB(cl3DLight));
                    end;
                    DrawText(hdc, PChar(s + ' '), len,
                        r, GetTextOptions(s) );
                end;

            end;
            oppacity := 1.0;


            if (ci <> nil) then begin
                if (ci.GetFormat in [CF_TEXT, CF_UNICODETEXT]) then begin
                    //oppacity := 0.6;
                end else begin
                    ClipTypeMark(hdc, rcItem, ClipTypeBitmap(ci), oppacity);
                end;
            end;
        end;

    end;



var hbTextBack, {hb2,} hbColumn, {hb4,} hbhighlight : HBRUSH;
    mi : TMenuItem;
    h : HGDIOBJ;
    oldf : HFONT;
    id, flags : integer;

    mitd : TMenuItemTagData;
    hilight, leftcolum, line, hotlight : TRect;

    vert : array[0..1] of TRIVERTEX;
    gRect   : GRADIENT_RECT;
    c, backc, colc : TColor;
    hue, lum, sat : word;
    can : TCanvas;
    icon : TIcon;
begin
    Windows.SetLastError(ERROR_SUCCESS);
    //UnitMisc.AppendLog('wmdraw');


    IsCurrentMenuItem := false;
    with msg.DrawItemStruct^ do begin
    dc := Windows.SaveDC(hDC);
    if (dc = 0) then begin
        UnitMisc.AppendLog('WMDrawItem: SaveDC failed - ', true);
        EXIT;
    end;
    oldf := Windows.SelectObject(hdc, self.Font.Handle);

    id := msg.drawitemstruct.itemID;
    flags := Windows.GetMenuState(
        msg.drawitemstruct.hwndItem,
        self.ItemIDToMenuPosition(id),
        MF_BYPOSITION
    );

    //
    // Fill the entire background with a hilight or no higlight
    //

    selected := not ((itemState and ODS_SELECTED) = 0);

    if  (not selected) then begin
        tc := Windows.SetTextColor(hDC, ColorToRGB(font.Color));


        //hb2 := Windows.CreateSolidBrush(ColorToRGB(clHighLight));
        //if (sysutils.Win32MajorVersion =5) and (sysutils.Win32MinorVersion=0)   then begin
        //    c := ColorToRGB(GetHighLightColor(clMenu));
        //    backc := ColorToRGB(GetHighLightColor(clMenu));
        //end else begin
            c := ColorToRGB(clMenu);
            bc := Windows.SetBkColor(hDC, ColorToRGB(clMenu));
            backc := ColorToRGB(clMenu);
        //end;

        {ColorRGBToHLS(c, hue,lum,sat);
        if lum < 210 then begin
            c := ColorHLSToRGB(hue,210,sat);
        end;
        ColorRGBToHLS(backc, hue,lum,sat);
        if lum < 210 then begin
            backc := ColorHLSToRGB(hue,210,sat);
        end;}
        hbTextBack := Windows.CreateSolidBrush(c);
        bc := Windows.SetBkColor(hDC, backc);
    end else begin
        tc := Windows.SetTextColor(hdc, ColorToRGB(clHighLightText));
        bc := Windows.SetBkColor(hdc, ColorToRGB(clHighlight));
        hbTextBack := Windows.CreateSolidBrush(ColorToRGB(clHighlight));
    end;
    c := ColorToRGB(clHighlight);
    ColorRGBToHLS(c,hue,lum,sat);
    c := ColorHLSToRGB(hue,max(lum-60,0),sat);
    hbhighlight := Windows.CreateSolidBrush(c);

    colc := ColorToRGB(clMenu);
    ColorRGBToHLS(colc,hue,lum,sat);
    colc := ColorHLSToRGB(hue,lum-15,sat);
    hbColumn := Windows.CreateSolidBrush(colc);



    // Does NOT work, only ODS_SELECTED ever seems to get sent
    if ((flags and (ODS_GRAYED or ODS_DISABLED {or ODS_INACTIVE})) <> 0) then begin
        tc := Windows.SetTextColor(hdc, ColorToRGB(clGrayText));
    end;
    if (hbTextBack <> 0) then begin

        if selected then begin
            SetBkMode(hdc, TRANSPARENT);
            Windows.FillRect(hdc,rcitem, hbhighlight);
            hilight.Top := rcitem.Top+1;
            hilight.bottom := rcitem.bottom-1;
            hilight.Left := rcitem.Left + LEFT_COLUMN_WIDTH+2;
            hilight.Right := rcitem.Right-2;
            //Windows.FillRect(hdc, hilight, hb);


              c := ColorToRGB(clhighlight);
              ColorRGBToHLS(c,hue,lum,sat);
              c := ColorHLSToRGB(hue,lum+15,sat);

              vert [0] .x      := hilight.left;
              vert [0] .y      := hilight.top;
              vert [0] .Red    := GetRValue(c) shl 8;
              vert [0] .Green  := GetGValue(c) shl 8;
              vert [0] .Blue   := GetBValue(c) shl 8;
              vert [0] .Alpha  := $00;

              c := ColorToRGB(clhighlight);
              ColorRGBToHLS(c,hue,lum,sat);
              c := ColorHLSToRGB(hue,lum-10,sat);

              {ColorRGBToHLS(c,hue,lum,sat);
              //showmessage(inttostr(lum));
              if lum < 140 then begin
                c := ColorHLSToRGB(hue,140,sat);
              end;
              }
              vert [1] .x      := hilight.Right;
              vert [1] .y      := hilight.bottom;
              vert [1] .Red    := GetRValue(c) shl 8;
              vert [1] .Green  := GetGValue(c) shl 8;
              vert [1] .Blue   := GetBValue(c) shl 8;
              vert [1] .Alpha  := $00;

              gRect.UpperLeft  := 0;
              gRect.LowerRight := 1;
              GradientFill(hdc, @vert,2,@gRect,1,GRADIENT_FILL_RECT_V);

        end else begin
            Windows.FillRect(hdc, rcItem, hbTextBack);
        end;
        leftcolum.Left := rcItem.left;
        leftcolum.Right := rcItem.left + LEFT_COLUMN_WIDTH;
        leftcolum.Top := rcitem.Top;
        leftcolum.Bottom := rcitem.Bottom;

        Windows.FillRect(hdc, leftcolum, hbColumn);

    end;

    try
        mi := self.ItemIDToMenuItem(itemID);
        TruncateCaption := false;
        try
            if mi.Tag <> 0 then begin

                mitd := TagDataList.Get(mi.tag);
                if mi <> nil then begin
                    if mi.checked then begin
                        DrawTextAndBitmap(mi.Bitmap, mi.caption);
                        can := TCanvas.Create;
                        can.Brush.Color := colc;
                        can.Handle := hdc;
                        can.Pen.Color := ColorToRGB(clWhite);
                        can.Rectangle(
                            rcitem.left+1,rcitem.top+1,
                            rcitem.left+1+LEFT_COLUMN_WIDTH-3,rcitem.bottom+1
                        );
                        can.Pen.Color := tc;
                        can.Rectangle(
                            rcitem.left,rcitem.top,
                            rcitem.left+LEFT_COLUMN_WIDTH-3,rcitem.bottom
                        );
                        GraphUtil.DrawCheck(
                            can,
                            point(
                                rcitem.left+5,
                                rcitem.top + trunc((rcitem.Bottom-rcitem.top)/2)),
                            3,true);
                        myfree(can);
                    end else begin
                        case mitd.itemtype  of
                        IT_CLIPBOARD:
                            begin
                                 //DrawTextAndBitmap(nil, mitd.prefix + PU_CURRENT_TEXT);
                                DrawMenuItem(mitd.prefix + PU_CURRENT_TEXT,frmmainpopup.CurrentClipboard, cardinal(-1));
                            end;
                        IT_SELECTED:
                            begin
                                DrawMenuItem(mitd.prefix, nil, 0, mitd.text);
                            end;
                        IT_LAST:
                            begin
                                DrawMenuItem(mitd.prefix + PU_LAST_TEXT,nil, mitd.icon, mitd.text);
                            end;
                        IT_SEARCH:
                            begin
                                DrawmenuItem(mitd.prefix,nil,0,mitd.text);
                            end;
                        IT_MENU, IT_TEMP, IT_CLICKED, IT_OTHER:
                            begin
                                if mitd.itemtype = IT_MENU then begin
                                    if mitd.clip <> nil then begin
                                        if mitd.clip.GetAsText = self.CurrentClipboard.GetAsText then begin
                                            IsCurrentMenuItem := true;
                                            //HiliteMenuItem(self.Handle, pop, 0, MF_HILITE or MF_BYPOSITION);
                                        end;
                                        DrawMenuItem(mitd.prefix, mitd.clip, mitd.icon,'', false);
                                    end;
                                end else begin
                                    DrawMenuItem(mitd.prefix, mitd.clip, mitd.icon,'', (mitd.itemtype = IT_TEMP));
                                end;
                                TruncateCaption := true;
                            end;
                        IT_CANCEL:
                            begin
                                DrawMenuItem('',nil,mitd.icon,mi.caption);
                            end;
                        IT_PROGRAM:
                            begin
                                DrawMenuItem('',nil,mitd.icon,mi.caption);
                            end;
                        IT_POPUP:
                            begin
                                if selected then begin
                                    {icon := BitmapToIcon(mi.Bitmap);
                                    if (icon = nil) then begin}
                                         DrawTextAndBitmap(mi.Bitmap, mitd.prefix + mi.caption);
                                    {end else begin
                                        DrawMenuItem(mitd.prefix,nil, icon.Handle, mi.caption, false);
                                        icon.Free;
                                    end;}
                                end else begin
                                    DrawTextAndBitmap(nil, mitd.prefix + mi.caption);
                                end;
                            end;
                        IT_PERMANENT:
                            begin
                                if selected then begin
                                    icon := BitmapToIcon(mi.Bitmap);
                                    if (icon = nil) then begin
                                         DrawTextAndBitmap(mi.Bitmap, mitd.prefix + mi.caption);
                                    end else begin
                                        DrawMenuItem(mitd.prefix,nil, icon.Handle, mi.caption, false);
                                        icon.Free;
                                    end;
                                end else begin
                                    DrawTextAndBitmap(nil, mitd.prefix + mi.caption);
                                end;
                            end;
                        IT_FULL:
                            begin
                                icon := BitmapToIcon(mi.Bitmap);
                                if (icon = nil) then begin
                                     DrawTextAndBitmap(mi.Bitmap, mitd.prefix + mi.caption);
                                end else begin


                                    DrawMenuItem(mitd.prefix,nil, icon.Handle, mi.caption, false);
                                    UpdateAccessKeys(mitd.prefix + mi.caption);
                                    icon.Free;
                                end;
                            end;
                        IT_LINE:
                            begin
                                line.Top := rcitem.Top + trunc((rcitem.bottom - rcitem.top)/2);
                                line.Bottom := line.top +1;
                                line.Right := rcitem.Right-4;
                                line.Left := rcitem.left+ LEFT_COLUMN_WIDTH + 1;
                                // Vista-Like version

                                if (sysutils.Win32MajorVersion <=5) then begin
                                    Windows.DrawEdge(hdc,line,BDR_RAISEDINNER Or BDR_SUNKENOUTER,BF_BOTTOM);
                                end else begin
                                    Windows.DrawEdge(hdc,line,BDR_SUNKENOUTER Or BDR_SUNKENINNER,BF_BOTTOM);
                                end;
                                // darker XP-Like version
                                //Windows.DrawEdge(hdc,line,BDR_SUNKENOUTER or BDR_RAISEDINNER,BF_BOTTOM);
                            end;
                        else
                            begin
                                DrawTextAndBitmap(mi.Bitmap, mitd.prefix + mi.caption);
                            end;
                        end;
                    end;
                end;
            end;
        except
            DrawTextAndBitmap(mi.Bitmap, mi.caption);
        end;

        Windows.DrawEdge(hdc,leftcolum,BDR_RAISEDINNER,BF_RIGHT);
        inc(leftcolum.Right);
        Windows.DrawEdge(hdc,leftcolum,BDR_SUNKENOUTER ,BF_RIGHT);

        rcitem.Left := 0;
        Windows.DrawEdge(hdc,rcitem,BDR_SUNKENINNER, BF_RIGHT);
        Windows.DrawEdge(hdc,rcitem,BDR_SUNKENOUTER, BF_RIGHT);

        //Windows.DrawEdge(hdc,rcitem,BDR_RAISEDINNER, BF_LEFT);

    finally
        h := SelectObject(hdc, oldf);
        {DeleteObject(h);
        }
        Windows.SetTextColor(hdc, tc);
        Windows.SetBkColor(hdc, bc);
        Windows.RestoreDC(hdc, dc);

        Windows.DeleteObject(hbhighlight);
        //Windows.DeleteObject(hb4);
        Windows.DeleteObject(hbColumn);
        //Windows.DeleteObject(hb2);
        Windows.DeleteObject(hbTextBack);

    end;
    end;
end;



//
// And Item was selected by keystroke, or by click
//
procedure TFrmDummyUnicodePopup.WMMenuChar(var Msg: TWMMenuChar);
var
    s : string;
begin
    if (msg.MenuFlag and MF_POPUP) <> 0 then begin
        UnitMisc.AppendLog('wwmenuchar');

        self.TabCommand := false;

        //
        // Wait until user releases keystrok
        // Translate key to ItemID
        // pass information WMCommand - as if it was clicked by a mouse
        //

        while KeyboardQuery.IsPressed(VkKeyScan(msg.user)) do begin
            Application.ProcessMessages;
            sleep(10);
        end;
        msg.Result := MakeLong(0, MNC_CLOSE);


        s := KeyToItemID.Values[lowercase(msg.User)];

        self.InClipTypeIcon := false;
        if (s <> '') then begin
            //
            // trigger the correct WMCommand event
            //



            Application.ProcessMessages;
            m.NotifyCode := 0;
            m.ItemID := StrToInt(s);
            m.Ctl := 0;

            self.delayCommand := true;
            timDelayedCommand.Enabled := true;
            //self.WMCommand(m);
        end else begin
            if msg.User = ' ' then begin
                m.ItemID := self.LastSelectID;
                m.NotifyCode := 0;
                m.Ctl := 0;

                self.delayCommand := true;
                timDelayedCommand.Enabled := true;
            end else if msg.user = chr(vk_tab) then begin
                m.NotifyCode := 0;
                m.Ctl := 0;
                m.ItemID := self.LastSelectID;
                self.TabCommand := true;
                
                self.delaycommand := true;
                timDelayedCommand.enabled := true;

            end;
        end;
    end else begin
        inherited;
    end;

    self.delayCommand := false;
end;


procedure TFrmDummyUnicodePopup.WMCommand(var Msg: TWMCommand);
var
    itemID : integer;
    mi2 : TMenuItem;
    mitd : TMenuItemTagData;
begin
    // Given an ItemID - carry out the selected action
    UnitMisc.AppendLog('command');

    if (msg.NotifyCode = 0) then begin
        itemID := msg.ItemID;
        mi2 := self.ItemIDToMenuItem(itemID);
        if (mi2 <> nil) then begin


            mitd := TagDataList.get(mi2.Tag);

            // Some items expect the menu item passed back
            // Other items need this form's handle to set focus correctly

            case mitd.itemtype  of
            IT_MENU, IT_TEMP,  IT_CLICKED:
                begin
                    if (self.InClipTypeIcon) or (self.TabCommand) then begin
                        FrmMainPopup.SendText('', mitd.clip, true);
                    end else if (self.RightClicked) then begin
                        msg.Result := 1;
                        FrmMainPopup.ShowItemMenu(mitd.clip.GetAsText,mitd.clip,mitd.itemtype=IT_TEMP);
                        {
                        FrmMainPopup.SetItemMenuOverride(true);
                        mi2.OnClick(mi2);}
                    end else if (self.InApplicationIcon) then begin
                        if frmConfig.cbEditClipWindow.checked then begin
                            FrmEditItem.SetText(mitd.clip.GetAsText, nil);
                            FrmMainPopup.ShowPreviewEditForm;
                        end else begin
                            frmEditTextExternal.EditClip(mitd.clip);
                        end;
                    end else begin
                        mi2.onclick(mi2);
                    end;
                end;
            IT_OTHER:
                begin
                    if (self.RightClicked) then begin
                        FrmMainPopup.SetItemMenuOverride(true);
                        mi2.OnClick(mi2);
                    {end else if (self.InApplicationIcon) then begin
                        FrmEditItem.SetText(mitd.clip.GetAsText, nil);
                        FrmMainPopup.ShowPreviewEditForm;}
                    end else begin
                        mi2.onclick(mi2);
                    end;
                end;
            IT_PERMANENT, IT_SEARCH:
                begin
                    if mitd.itemtype in [IT_PERMANENT]  then begin
                        if self.RightClicked  then FrmMainPopup.SetItemMenuOverride(true);
                    end;
                    if self.InApplicationIcon  then begin
                        FrmPermanent.ShowAndSelect(mitd.PermanentGroupID, mitd.itemindex);  
                    end else begin
                        mi2.OnClick(mi2);
                    end;
                end;
            IT_CLIPBOARD:
                begin
                    if self.RightClicked  then FrmMainPopup.SetItemMenuOverride(true);
//                    FrmMainPopup.CurrentMenuItemClickEvent(self, false);
                end;
            IT_LAST:
                begin
                    if self.RightClicked  then FrmMainPopup.SetItemMenuOverride(true);
                  //  FrmMainPopup.LastMenuItemClickEvent(self);
                end;
            IT_SELECTED:
                begin
                    if self.RightClicked then FrmMainPopup.SetItemMenuOverride(true);
                    mi2.OnClick(mi2);
                end;
            IT_FULL:
                begin
                    if self.InApplicationIcon then begin

                        FrmConfig.pcPanels.ActivePage := FrmConfig.tsMenuOrder;
                        frmConfig.Show;
                        UnitMisc.ForceForeground(frmconfig.Handle);

                    end else begin
                        mi2.OnClick(mi2);
                    end;
                end
            else
                begin
                    mi2.OnClick(mi2);
                end;
            end;
            FrmMainPopup.SetItemMenuOverride(false);
            Paste.ClearOnceFlags;
            // The only reliable place to execute code on the dismissal of the
            // popup after events have been dispatched. 
        end;
    end else begin
        {dunno if this is needed}
        inherited;
    end;

    self.TabCommand := false;
end;




{
//
// These 3 messages control the menu popup
// Item is selected, Idle state occurs, then the timer will fire the
// showtooltip event
}

procedure TFrmDummyUnicodePopup.WMEnterIdle(var Msg: TWMEnterIdle);
var pos : TPoint;

    function equal(p1, p2 : TPoint) : boolean;
    begin
        result := p1.x = p2.x;
        result := result and (p1.y = p2.y);
    end;

var  r : TRect;
begin
    //UnitMisc.AppendLog('wmenteridle');

    // Major "Clever" Trick
    // the popup menu doesn't response to right-clicks and other
    // keyboard presses, but I can test them on an "enter idle" event;

    self.RightClicked := false;
    if KeyboardQuery.IsClicked(rightButton) then begin
        self.RightClicked := true;
    end;
    if FirstEnterIdle then begin
        FirstEnterIdle := false;
        //mysleep(150);
        //Paste.SendDOWNSpecial;
        //EXIT;
    end;
    // detect a release of the winkey and fire a paste
    // if the hotkey is pressed, ignore the winkey release
    {if WinkeyDetected and not KeyboardQuery.IsPressed(VK_LWIN) then begin
        WinkeyDetected := false;
        if not HotkeyCharDetected then begin

            Application.ProcessMessages;
            Application.ProcessMessages;
            //paste.SendESC;
            //sleep(300);
            //paste.SendSPACE;
            timDelayedSpace.Enabled := true;
            EXIT;
        end else begin
            HotkeyCharDetected := false;
        end;
    end else begin
        WinkeyDetected := KeyboardQuery.IsPressed(VK_LWIN) and not KeyboardQuery.IsPressed(fkey);
        HotkeyCharDetected := HotkeyCharDetected or KeyboardQuery.IsPressed(fkey);
        if FirstEnterIdle and WinkeyDetected then begin
            WinkeyDetected := false;
        end else begin
            FirstEnterIdle := false;
        end;
        Application.ProcessMessages;
    end;
    }


    timShow.Interval := Application.HintPause;

    self.LastCursorPos  := mouse.CursorPos;
    pos := self.LastCursorPos;
    self.LastInApplicationIcon := self.InApplicationIcon;



    if self.LastIsFullConfigureItem or self.LastIsPermItem then begin
        inc(pos.X, 30);
        if self.LastIsFullConfigureItem  then
            Tooltip.CloseTooltip;
        InApplicationIcon := false;
        if self.MouseInApplicationIcon  then begin
            if self.LastIsPermItem  then
                Tooltip.CloseTooltip;

            if self.LastIsFullConfigureItem  then
                PasteTooltip.ShowTooltip('Click here to Edit', pos, false);

            InApplicationIcon := true;
        end;
        if not InApplicationIcon then begin
            PasteTooltip.CloseTooltip;
        end;

        if self.LastIsFullConfigureItem  then
            EXIT;
    end;

    if ((LastClipItem = nil) and (lasthint = '')) then begin
        EXIT;
    end;


    if self.LastInApplicationIcon <> self.InApplicationIcon  then begin
        PasteTooltip.CloseTooltip();
    end;



    if PasteTooltip.Showing then begin
       self.DisplayTootlip;
    end else begin
        inc(pos.X, 30);
        pos.Y := self.LastMenuRect.Bottom - 20;

        InClipTypeIcon := false;
        InApplicationIcon := false;

        if self.MouseInCliptypeIcon then begin
            Tooltip.CloseTooltip;
            PasteToolTip.ShowTooltip(
                'Press TAB or Click here to paste as ' + self.LastClipItem.GetFormatName,
               pos, false );
            InClipTypeIcon := true;
        end else if self.MouseInApplicationIcon  then begin
            Tooltip.CloseTooltip;

            self.ItemMenuCreate;
            if fim.Top <> lastmenurect.top then begin
                fim.Top := lastmenurect.top-2;
                fim.Left := LastMenuRect.Left - fim.Width-4;
            end;
            timShowClipMenu.Enabled := true;

            InApplicationIcon := true;
        end else begin
            PasteTooltip.CloseTooltip;

            timShow.enabled := false;
            if self.MouseHighlighted  then begin
                r := self.LastMenuRect;
                r.left := r.left + ICON_SPACER;
                timShow.Enabled := windows.PtInRect(r, mouse.CursorPos);
                if timShow.Enabled then begin
                    self.ClearItemMenu;
                end;
            end else begin
                timshow.Enabled := true;

                r := self.LastMenuRect;
                r.left := r.left + ICON_SPACER;
                if windows.PtInRect(r, mouse.CursorPos) then begin
                    self.ClearItemMenu;
                end;
            end;


        end;

        if 0 in [self.LastMenuRect.Top, lastmenurect.Left] then

        if not ((Windows.PtInRect(self.LastMenuRect, mouse.CursorPos)) and
            (mouse.CursorPos.x < self.LastMenuRect.left + (ICON_SPACER-2))) then begin
            UnitMisc.AppendLog('Hiding ItemMenu');
            self.ClearItemMenu;


        end;
    end;


    self.LastIdlePos := self.LastCursorPos;
    timclose.interval := 0;
    timclose.Interval := 2000;


    if KeyboardQuery.IsPressed(VK_APPS) then begin
        self.RightClicked := true;
        Paste.SendSPACE;

        {
        charmsg.MenuFlag :=  MF_POPUP;
        charmsg.User := chr(VK_SPACE);
        charmsg.Msg := WM_MENUCHAR;
        self.WMMenuChar(charmsg);}

        {Application.ProcessMessages;
        m.NotifyCode := 0;
        m.ItemID := LastSelectID;
        m.Ctl := 0;

        self.delayCommand := true;
        tim2.Enabled := true;

        Windows.PostMessage(self.Handle, WM_NULL, 0, 0);
        }

    end;

end;



procedure TFrmDummyUnicodePopup.timShowClipMenuTimer(Sender: TObject);
var r : TRect;
begin


    timShowClipMenu.Enabled := false;

    r := self.LastMenuRect;
    r.Right := r.Left + ICON_SPACER;
    if Windows.PtInRect(r,  mouse.CursorPos) then begin
        fim.Show;
    end;
end;

procedure TFrmDummyUnicodePopup.timShowTimer(Sender: TObject);
begin
    timShow.Enabled := false;
    Tooltip.EnforceSizeRestriction(true);
    if self.TimeFiring then begin
        UnitMisc.AppendLog('Timer Conflict');
        self.TimeFiring := false;
        EXIT;
    end;
    self.TimeFiring  := true;
    UnitMisc.AppendLog('TimerFired');


    if ((self.LastClipItem = nil) and (self.LastHint = ''))   then begin
        self.TimeFiring := false;
        EXIT;
    end;

    UnitMisc.AppendLog('ItemMenu.visible=' + booltostr(fim.Visible));

    if not tooltip.IsShowing then begin
        TimAutoGrow.Enabled := false;
        TimAutoGrow.Enabled := true;
    end;

    self.DisplayTootlip;
    if not tooltip.SizeWasRestricted then TimAutoGrow.Enabled := false;
    


    self.LastHint := '';
    self.TimeFiring := false;
end;

function TFrmDummyUnicodePopup.ItemIDToMenuPosition(
  itemID: cardinal): integer;
var mi : TMenuItem;
begin
    result := -1;
    mi := self.ItemIDToMenuItem(itemID);
    if (mi <> nil) and (mi.Parent <> nil) then begin
        result := mi.Parent.IndexOf(mi);
    end;
end;



procedure TFrmDummyUnicodePopup.ItemMenuCreate;
begin
    if fim = nil then begin
        fim := TClipMenu.CreateParented(self.handle);
        fim.Name := '';
        fim.ParentWindow := GetDesktopWindow;
        self.ClearItemMenu; 
    end;
end;

procedure TFrmDummyUnicodePopup.ItemMenuDelayedClose;
begin
    fim.TriggerDelayedClose;
end;

procedure TFrmDummyUnicodePopup.ItemMenuPermanentClipMode;
begin
    self.ItemMenuCreate;
    fim.SetPermanentClipMode;
end;

procedure TFrmDummyUnicodePopup.ItemMenuPopupClipMode;
begin
    self.ItemMenuCreate;
    fim.SetPopupClipMode;
end;

//
// Used to detect when a new item is entered
// - extract the position rectangle and clip data
//
procedure TFrmDummyUnicodePopup.WMMenuSelect(var Msg: TWMMenuSelect);
    procedure ClearLastCache;
    begin
        self.LastHint := '';
        self.LastClipITem := nil;
        self.LastIsMenuItem := false;
        self.LastIsFullConfigureItem := false;
        self.LastIsPermItem := false;
        self.LastPermGroup := -1;
        self.LastPermIndex := -1;
    end;
var id : word;
    i  : integer;
    mi : TMenuItem;
    r : Trect;
    mitd : TMenuItemTagData;
begin
    //UnitMisc.AppendLog('wmselect ' + IntToStr(msg.MenuFlag) );
    timShow.Enabled := false;
    {if ((msg.MenuFlag and MF_HILITE) =0)  then begin
        UnitMisc.AppendLog('No Highlight Select Message');
        EXIT;
    end;
    }
    if msg.menuflag = $FFFF then begin
        EXIT;
    end;
    ClearLastCache;
    self.PopupInited := true;
    
    // detect a hilight or de-hilight message
    // disable on de-hilight
    // detect mouse hilighting
    if (msg.MenuFlag and MF_HILITE) <> 0 then begin
        MouseHighlighted := (msg.MenuFlag and MF_MOUSESELECT) <> 0;
    end else begin
        timShow.Enabled := false;
    end;

    //self.LastIsFullConfigureItem := false;

    //
    // Find the MenuItem by the unique ID and then use it to
    // find the actual index of the menu item
    //
    // Use the mouse position, if it's within the MenuItem otherwise
    // place it directly next to the menu
    //
    id := TWMMENUSELECT(msg).IDItem;
    LastSelectID := id;
    mi := self.ItemIDToMenuItem(id);
    if (mi <> nil) and (mi.Parent <> nil) then begin
        i := mi.Parent.IndexOf(mi);
        if not Windows.GetMenuItemRect(0, {pop}msg.Menu, i, r) then begin
            UnitMisc.AppendLog('MenuRect Failed', true);
        end else begin
            self.LastMenuRect := r;
        end;
    end else begin
        UnitMisc.AppendLog('No MenuRect');
    end;







    if mi = nil then begin
        UnitMisc.AppendLog('No associated TMenuItem');
        Tooltip.CloseTooltip;
        PasteTooltip.CloseTooltip;
        EXIT;
    end;
    mitd := TagDataList.Get(mi.tag);


    // hide the menu item only when it changes to another
    // viable target - it will be hidden for non-viable targets
    // elsewhere
    if (mi <> self.LastMenuItem) then begin
        if not (mitd.itemtype in [IT_MENU,IT_PERMANENT]) then begin
            self.ClearItemMenu;
        end else if not self.LastInApplicationIcon then begin
            self.ClearItemMenu;
        end;
    end;

    //
    // ignore Menu items with submenus - they don't have tooltips
    //
    if ((TWMMENUSELECT(msg).MenuFlag and MF_POPUP) <> 0) then begin
        Tooltip.CloseTooltip;
        PasteTooltip.CloseTooltip;
        EXIT;
    end;


    if (mi <> self.LastMenuItem) or
        (self.InApplicationIcon <> self.LastInApplicationIcon) then begin
        // need to redisplay when a new item is selected
        Tooltip.CloseTooltip;
    end;
    self.LastMenuItem := mi;
    PasteTooltip.CloseTooltip;

    //
    // gather the clip, the tooltip hint to show, and detect Menu Items
    //
    LastHotkeyName := '';
    try
        case mitd.itemtype  of
            IT_NONE: ;
            IT_FULL:
                begin
                    self.LastIsFullConfigureItem := true;
                    //self.LastHint := mi.caption;
                end;
            IT_MENU:
                begin
                    fim.SetPopupClip(mitd.clip);
                    self.LastClipItem := mitd.clip;
                    self.LastIsMenuItem := true;
                end;
            IT_OTHER:
                self.LastClipItem := mitd.clip;
            IT_CLICKED:
                self.LastClipItem := mitd.clip;
            IT_PERMANENT:
                begin
                    //UnitMisc.AppendLog('Permanent Select');

                    fim.SetPermanentClip(mitd.PermanentGroupID, mitd.itemindex);

                    mitd := TagDataList.Get(mi.tag);
                    self.LastIsPermItem := true;
                    self.LastHint := mitd.text;
                    self.LastPermGroup := mitd.PermanentGroupID;
                    self.LastPermIndex := mitd.itemindex;

                    if (mitd.PermanentGroupID  <> - 1) then begin
                        FrmPermanent.PermFolderPush;
                        FrmPermanent.SetPermanentPath(FrmPermanent.PermFoldersGetItem(mitd.PermanentGroupID));
                        self.LastHint := mitd.text;

                        // clean up leaks
                        // GetComplexItem generates a new ClipItem every call
                        if FrmPermanent.IsComplexItem(self.LastHint) then begin
                            if self.LastClipItem <> nil then begin
                                myfree(self.lastclipitem);
                            end;
                            self.LastClipItem := FrmPermanent.GetComplexItem(mitd.text) ;
                        end;

                        LastHotkeyName := FrmPermanent.GetItemHotkeyName(mitd.itemindex);
                        FrmPermanent.PermFolderPop;
                    end else begin
                        LastHotkeyName := FrmPermanent.GetItemHotkeyName(mitd.itemindex);
                    end;
                end;

            IT_PERMANENT_GROUP: ;
            IT_POPUP: ;
            IT_TEMP:
                begin
                    self.LastClipItem := mitd.clip;
                    if (self.LastClipItem = nil) then begin
                        self.LastHint := mitd.text;
                    end;
                end;

            IT_SWITCH: ;
            IT_SYS_SWITCH: ;
            IT_PASTE_METHOD: ;
            IT_CLIPBOARD:
                self.LastClipItem := self.CurrentClipboard;
            IT_LAST:
                self.LastHint := FrmMainPopup.GetLastStringSelected;
            IT_PROGRAM: ;
            IT_SELECTED:
                self.LastHint := FrmMainPopup.GetSelectedText;
        end;
        {
        timShow.Enabled := false;
        if (msg.MenuFlag and MF_MOUSESELECT) = 0 then begin
            timShow.Enabled := true;
        end else begin
            timShow.Enabled := windows.PtInRect(self.LastMenuRect,mouse.CursorPos);
        end;}
    finally

    end;
end;


procedure TFrmDummyUnicodePopup.IMDeleteClick(Sender: Tobject);
begin

end;

procedure TFrmDummyUnicodePopup.IMDestroyClick(Sender: Tobject);
begin

end;

procedure TFrmDummyUnicodePopup.IMEditClick(Sender: TObject);
begin




end;

procedure TFrmDummyUnicodePopup.IMPasteClick(Sender: Tobject);
begin

end;

procedure TFrmDummyUnicodePopup.IMPermanentClick(Sender: Tobject);
begin

end;

function TFrmDummyUnicodePopup.ItemIDToMenuItem(
  itemID: cardinal): TMenuItem;
var s : string;
    i : integer;
begin
    result := nil;

    // too spammy for normal use
   // UnitMisc.AppendLog('ItemIDToMenu: ' + IntToStr(ItemID));


    // either ItemID is a WM_COMMAND (an index to an object)
    // or it's an address of a menu item
    // - find the correct one and return it or NIL

    if (itemID < Cardinal(SubObjectList.Count)) then begin
        result := TMenuItem(SubObjectList.Items[itemID]);
    end;

    if result = nil then begin
        s := SubMenuItemList.values[IntToStr(itemID)];
        if (s <> '') then begin
            i := StrToInt(s);
            if i < SubObjectList.Count then begin
                result := TMenuItem( SubObjectList.items[i] );
            end;
        end;
    end;
end;

procedure TFrmDummyUnicodePopup.timDelayedCommandTimer(Sender: TObject);
begin
    if (not self.delayCommand) then begin
        timDelayedCommand.Enabled := false;
        self.WMCommand(m);
    end;
end;

procedure TFrmDummyUnicodePopup.timDelayedSpaceTimer(Sender: TObject);
begin
    timDelayedSpace.Enabled := false;
    paste.SendSPACE;
end;

procedure TFrmDummyUnicodePopup.TimAutoGrowTimer(Sender: TObject);
begin
    TimAutoGrow.Enabled := false;
    timShow.Enabled := false;

    if (self.LastClipItem =nil) and (self.lasthint= '') then begin
        EXIT;
    end;

    //self.AutoGrowShowing := true;
    Tooltip.CloseTooltip();
    Tooltip.EnforceSizeRestriction(false);
    self.DisplayTootlip;
    unitmisc.AppendLog('AutoGrow');
end;

procedure TFrmDummyUnicodePopup.TimCloseTimer(Sender: TObject);
begin
    timclose.Enabled := false;
    UnitMisc.AppendLog('TimClose');
end;


function TFrmDummyUnicodePopup.MouseInApplicationIcon: boolean;
begin
    result :=
        (Windows.PtInRect(self.LastMenuRect, mouse.CursorPos)) and
        (mouse.CursorPos.x < self.LastMenuRect.left + (ICON_SPACER-2)) and
        (((self.LastClipItem <> nil) and self.LastIsMenuItem)
            or (self.LastIsFullConfigureItem or self.LastIsPermItem) ) ;
end;

function TFrmDummyUnicodePopup.MouseInCliptypeIcon: boolean;
begin
    result :=
        (Windows.PtInRect(self.LastMenuRect, mouse.CursorPos)) and
        (mouse.CursorPos.x > self.LastMenuRect.right - 15) and
        (self.LastClipItem <> nil) and
        not (self.LastClipItem.GetFormat in [CF_UNICODETEXT, CF_TEXT]);
end;

end.
