unit UnitClipMenu;
{
	NOTES:
        Keyboard focus is not working
}
interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
  Dialogs, Buttons, ExtCtrls, Menus, UnitClipQueue, Generics.collections;

type
TCustomButtonData = record
    caption : string;
    clip : string;
    button : TSpeedButton;
end;
type
  TClipMenu = class(TcustomPanel)
    {$REGION 'default'}
    btnPin: TSpeedButton;
    btnEdit: TSpeedButton;
    btnDelete: TSpeedButton;
    btnDestroy: TSpeedButton;
    btnPaste: TSpeedButton;
    btnPasteAs : TSpeedButton;
    btnPasteAsFile : TSpeedButton;
    btnPermanent: TSpeedButton;
    btnConfigure : TSpeedButton;

    btnCustomLine: TSpeedButton;
    btnCustom1: TSpeedButton;
    btnCustom2: TSpeedButton;
    btnCustom3: TSpeedButton;

    pmPaste: TPopupMenu;
    pnlPaste : TFlowPanel;


    btnMimic : TSpeedButton;
    btnShiftInsert : TSpeedButton;
    btnCtrlV : TSpeedButton;
    btnClipboard : TSpeedButton;

    btnAsPlain : TSpeedbutton;
    btnAsFile : TSpeedButton;
    btnAsFormat : TSpeedButton;

    pmPermanent: TPopupMenu;
    btnAsPlaintext : TSpeedButton;

    btnAsClip : TSpeedButton;

    MimickTyping1: TMenuItem;
    ShiftInsert1: TMenuItem;
    CTRLV1: TMenuItem;
    ClipboardOnly1: TMenuItem;
    AsFile1 : TMenuItem;
//    timClose: TTimer;
    asPlaintext1: TMenuItem;
    asClip1: TMenuItem;
    N1: TMenuItem;
    Cancel1: TMenuItem;
    N2: TMenuItem;
    Cancel2: TMenuItem;
    procedure CreateParams(var Params: TCreateParams);  override;
    procedure btnPinClick(Sender: Tobject);
    procedure btnEditClick(Sender: TObject);
    procedure btnPasteClick(Sender: TObject);
    procedure btnPasteAsClick(Sender: TObject);
//    procedure timCloseTimer(Sender: TObject);
    procedure MimickTyping1Click(Sender: TObject);
    procedure ShiftInsert1Click(Sender: TObject);
    procedure CTRLV1Click(Sender: TObject);
    procedure ClipboardOnly1Click(Sender: TObject);
    procedure AsPlainClick(Sender: TObject);
    procedure AsFile1Click(Sender: TObject);
    procedure AsFormatClick(Sender: TObject);
    procedure btnPermanentClick(Sender: TObject);
    procedure asPlaintext1Click(Sender: TObject);
    procedure asClip1Click(Sender: TObject);
    procedure btnDeleteClick(Sender: TObject);
    procedure btnDestroyClick(Sender: TObject);
    procedure btnMoreClick(Sender: TObject);
    {$ENDREGION}
  private
    { Private declarations }
    fgroup, findex : integer;
    fci : TClipItem;
    isPopup : boolean;

    fOnEditClip,
    fOnRemoveClip,
    fOnDestroyClip,
    fOnPasteClip,
    fOnMakePermanentClip,
    fOnFormMode,
    fOnHide : TNotifyEvent;
    fShowing : boolean;

    custombuttons : TList<TSpeedButton>;

    controlorder : array [0..100] of TControl;
    controlordercount : integer;
    controlhover : integer;

    ShowMoreMode : boolean;
//    procedure WMGetDlgCode(var Msg: TMessage); message WM_GETDLGCODE;
    procedure KeyDown(var Key: Word; Shift: TShiftState); reintroduce; override;
    procedure HandleButtonClick;
    procedure HideSubmenus;
    procedure FixPosition;
    procedure ExpandPasteAsMenu;
    procedure RedoOrder;
    procedure HandleCustomButton(Sender: TObject);
    procedure MouseEnterEvent(Sender: TObject);
  protected
    procedure paint; override;
    procedure KeyPress(var Key: Char); override;
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    property OnEditClip : TNotifyEvent read fOnEditClip write fOnEditClip;
    property OnRemoveClip : TNotifyEvent read fOnRemoveClip write fOnRemoveClip;
    property OnDestroyClip : TNotifyEvent read fOnDestroyClip write fOnDestroyClip;
    property OnPasteClip : TNotifyEvent read fOnPasteClip write fOnPasteClip;
    property OnMakePermanentClip : TNotifyEvent read fOnMakePermanentClip write fOnMakePermanentClip;
    property OnFormMode : TNotifyEvent read fOnFormMode write fOnFormMode;
    property OnHide : TNotifyEvent read fOnHide write fOnHide;

    property showing read fShowing;
    procedure SetPopupClip(ci : TClipItem);
    procedure SetACPopupItem(p : TObject);
    function GetACPopupItem : TObject;
    procedure SetPermanentClip(group, index : integer);
    procedure SetPopupClipMode;
    procedure SetPermanentClipMode;
    //procedure TriggerDelayedClose;
    procedure Show;
    procedure Hide;

    procedure ReportKeypress(key : Char); overload;
    procedure ReportKeypress(key : Word); overload;
    procedure SetCustomButton(index: integer; caption, clip : string);

    function MouseInMenu : boolean;

    property Font;
end;

implementation

uses UnitFrmConfig, UnitFrmMainPopup, UnitFrmEditItem, UnitFrmPermanentNew,
  UnitFrmEditTextExternal, UnitPopupGenerate, UnitMisc, UnitPaste, UnitFramePopup,
  UnitMenuItemTagdata, Clipbrd, UnitDropFile, unitfrmClipboardManager, UnitToken,
  StrUtils, UnitACPopupClicks, Math, UnitKeyboardQuery, UnitClipDatabase,
  UnitFrmEditHistory, UnitFocusManager;

{ $R *.dfm}

var acpopupitem : TACPopupItem;
// just a dirty trick to get around a circular unit reference
// can't add this to the Interface

procedure TClipMenu.ReportKeypress(key : Word);
    function IncHover(i : integer) : integer;
    begin
        result := controlhover;
        repeat
            result := (result + i) mod controlordercount;
            if result = -1 then result := controlordercount-1;
        until (controlorder[result] is TSpeedButton) and
            TSpeedButton(controlorder[result]).Visible and
            TSpeedButton(controlorder[result]).Enabled;
    end;
begin
    case key of
    VK_ESCAPE, VK_LEFT, VK_RIGHT: begin
        self.Hide;
    end;
    VK_UP:  begin
        TSpeedButton(controlorder[controlhover]).Flat := true;
        controlhover := IncHover(-1);
        TSpeedButton(controlorder[controlhover]).Flat := false;
    end;
    VK_DOWN: begin
        TSpeedButton(controlorder[controlhover]).Flat := true;
        controlhover := IncHover(+1);
        TSpeedButton(controlorder[controlhover]).Flat := false;
    end;
    VK_RETURN:
        begin
            try
                TSpeedButton(controlorder[controlhover]).Click;
            except
            end;
        end;
    else
        ReportKeypress(char(key));
    end;
end;
procedure TClipMenu.ReportKeypress(key : Char);
var c : Char;
    i,j,k : integer;
begin
    c := lowercase(key)[1];
    case c of
    'l' : self.btnPin.Click;
    'e' : self.btnEdit.Click;
    'u' : Self.btnPaste.Click;
    'p' : Self.btnPasteAs.Click;
    'm' : self.btnPermanent.Click;
    'd' : self.btnDelete.Click;
    's' : self.btnDestroy.Click;

    't' : self.btnMimic.Click;
    'i' : self.btnShiftInsert.Click;
    'v' : self.btnCtrlV.Click;
    'o' : Self.btnClipboard.Click;
    'f' : Self.btnAsFile.Click;

    'n' : self.btnAsPlain.Click;
    'x' : self.btnAsPlaintext.Click;
    'a' : self.asClip1.Click;
    'r' : self.btnAsFormat.Click;

    'h' : self.btnConfigure.Click;

    // default key
    ' ' :
        begin
            try
                TSpeedButton(controlorder[controlhover]).Click;
            except
            end;
        end;
    else
        begin
            try
                i := StrToInt(c);
                k := 0;
                for j := 0 to custombuttons.Count-1 do begin
                    if custombuttons[j].visible then inc(k);
                    if i=k then begin
                        custombuttons[j].click;
                        BREAK;
                    end;
                end;
            except
            end;
        end;
    end;
end;

function TClipMenu.MouseInMenu : boolean;
begin
    result := PtInRect(self.BoundsRect, mouse.CursorPos)
end;

const SUBMENU_PREFIX = '    ';

type TMySpeedButton = class(TSpeedButton)
    protected
        procedure paint; override;
    public
        property Canvas;
end;
procedure TMySpeedbutton.Paint;
var s : string;
    r : TREct;
begin
    r := self.ClientRect;
    dec(r.Bottom);
    canvas.Brush.Color := clBtnFace;
    if enabled then begin
        self.Font.Color := clBtnText;
        if PtInRect(self.ClientRect, self.ScreenToClient(mouse.CursorPos)) then begin
            if keyboardQuery.IsClicked(leftButton) then begin
                canvas.Brush.Color := blend(clMenu,clblack,90);
            end else begin
                canvas.Brush.Color := blend(clHighlight, clBtnFace, 8);
            end;
            canvas.Pen.Color := blend(clHighlight, clBtnFace, 48);
            canvas.RoundRect(r,8,6);
        end;
        if not flat then begin
            canvas.Brush.Color := clBtnFace;
            canvas.DrawFocusRect(r);
        end;

        SetBkMode(Canvas.Handle, Windows.TRANSPARENT);
    end else begin
        self.Font.Color := clGrayText;
    end;

    s := caption;
    r := self.ClientRect;
    inc(r.Left, Margin);
    canvas.Font := self.Font;


    DrawTextW(
        Canvas.Handle,
        pwidechar(s),length(s),
        r,
        DT_VCENTER or DT_SINGLELINE
    );


end;

constructor TClipMenu.Create(AOwner: TComponent);
    procedure SaveOrder(c : TControl);
    begin
        controlorder[controlordercount] := c;
        inc(controlordercount);
    end;
	procedure SetCommon(b : TSpeedButton; caption : string);
    begin
        b.Align := alTop;
        b.Layout := blGlyphLeft;
        b.Flat := true;
        b.Margin := 10;
        b.Margins.Top := 0;
        b.Margins.Bottom := 0;
        b.Spacing := 0;
        b.Spacing :=1;
    	b.caption := caption;
        b.AlignWithMargins := true;
        b.Height := b.Height - 4;   // Buttons are a bit too tall by default


        self.insertcontrol(b);
        saveorder(b);
    end;
    procedure SetCommon2(b : TSpeedButton; caption : string);
    begin
        b.Align := alTop;
        b.Layout := blGlyphLeft;
        b.Flat := true;
        b.Margin := 10;
        b.Margins.Top := 0;
        b.Margins.Bottom := 1;
    	b.caption := caption;
        b.AlignWithMargins := true;

//        self.insertcontrol(b);
    end;
    procedure SetCommonSubButton(b : TSpeedButton; caption : string);
    begin
        SetCommon(b, caption);
    	b.caption := SUBMENU_PREFIX + caption;
        b.Height := b.Height - 3;
    end;
    procedure SetCommonSubButton2(b : TSpeedButton; caption : string);
    begin
        SetCommon2(b, caption);
    	b.caption := SUBMENU_PREFIX + caption;
        b.Height := b.Height - 5;
    end;
    procedure NewLine;
    var sh : TShape;
    begin

        sh := TShape.Create(self);
        sh.Align := alTop;
        sh.Shape := stRectangle;
        sh.Height := 1;
        sh.Width := 110;
        sh.AlignWithMargins := true;
        sh.Margins.Top := 0;
        sh.Margins.Left := 4;
        sh.Margins.Right := 4;
        sh.Margins.Bottom := 1;

        sh.Pen.Color := dimColor(clBtnFace,0.88);

        self.InsertControl(sh);
        SaveOrder(sh);

        sh := TShape.Create(self);
        sh.Align := alTop;
        sh.Shape := stRectangle;
        sh.Height := 1;
        sh.Width := 110;
        sh.AlignWithMargins := true;
        sh.Margins.Top := 0;
        sh.Margins.Left := 4;
        sh.Margins.Right := 4;
        sh.Margins.Bottom := 1;
        sh.Pen.Color := dimColor(clBtnFace,1.5);

        self.InsertControl(sh);
        SaveOrder(sh);
    end;
var i : integer;
begin
    visible := false;
	inherited create(nil);
    visible := false;

    //
    // Workaround to get his control to show without stealing keyboard focus
    //
    self.Name := '';
    self.ParentWindow := GetDesktopWindow;

    self.ControlStyle := self.controlstyle- [csNoStdEvents];

    self.AlignWithMargins := true;

    controlordercount := 0;
    controlhover := 0;

    btnPin := TMySpeedbutton.Create(self);
    btnPin.OnClick := self.btnPinClick;
    SetCommon(btnPin, 'P&in to List');

    NewLine;

    btnEdit := TMySpeedbutton.Create(self);
    btnEdit.OnClick := self.btnEditClick;
    SetCommon(btnEdit, '&Edit');

    btnDelete := TMySpeedbutton.create(self);
    btnDelete.OnClick := self.btnDeleteClick;
    SetCommon(btnDelete,'&Delete');

    btnDestroy := TMySpeedbutton.Create(self);
    btnDestroy.OnClick := self.btnDestroyClick;
    SetCommon(btnDestroy, 'De&stroy');

    NewLine;

    btnPaste := TMySpeedbutton.Create(self);
    btnPaste.OnClick := self.btnPasteClick;
    SetCommon(btnPaste,'Paste &Using >');
        btnMimic := TMySpeedbutton.Create(self);
        btnMimic.OnClick := self.MimickTyping1Click;
        SetCommonSubButton(btnMimic,'Mimic &Typing');

        btnShiftInsert := TMySpeedbutton.Create(self);
        btnShiftInsert.OnClick := self.ShiftInsert1Click;
        SetCommonSubButton(btnShiftInsert,'Shift+&Insert');

        btnCtrlV := TMySpeedbutton.Create(self);
        btnCtrlV.OnClick := self.CTRLV1Click;
        SetCommonSubButton(btnCtrlV,'Ctrl+&V');

        btnClipboard := TMySpeedbutton.Create(self);
        btnClipboard.OnClick := self.ClipboardOnly1Click;
        SetCommonSubButton(btnClipboard,'Clipboard &Only');

    btnPasteAs := TMySpeedbutton.Create(self);
    btnPasteAs.OnClick := self.btnPasteAsClick;
    SetCommon(btnPasteAs, '&Paste As >');

        btnAsPlain := TMySpeedbutton.Create(self);
        btnAsPlain.OnClick := self.AsPlainClick;
        SetCommonSubButton(btnAsPlain,'Plai&ntext Only');
        btnAsPlain.Visible := false;

        btnAsFormat := TMySpeedbutton.Create(self);
        btnAsFormat.OnClick := self.AsFormatClick;
        SetCommonSubButton(btnAsFormat,'Fo&rmat ');
        btnAsFormat.Visible := false;


        btnAsFile := TMySpeedbutton.Create(self);
        btnAsFile.OnClick := self.AsFile1Click;
        SetCommonSubButton(btnAsFile,'&File');


    btnPasteAsFile := TMySpeedbutton.Create(self);
    btnPasteAsFile.OnClick := self.AsFile1Click;
    SetCommon(btnPasteAsFile, 'Paste As: &File');
    btnPasteAsFile.Visible := false;

    btnPermanent := TMySpeedbutton.Create(self);
    btnPermanent.OnClick := self.btnPermanentClick;
    SetCommon(btnPermanent,'&Make Permanent >');

        btnAsPlaintext := TMySpeedbutton.Create(self);
        btnAsPlaintext.OnClick := self.asPlaintext1Click;
        SetCommonSubButton(btnAsPlaintext,'as Plainte&xt');

        btnAsClip := TMySpeedbutton.Create(self);
        btnAsClip.OnClick := self.asClip1Click;
        SetCommonSubButton(btnAsClip,'&as Clip');

    NewLine;

    btnConfigure := TMySpeedButton.Create(self);
    btnConfigure.OnClick := self.btnMoreClick;
    SetCommon(btnConfigure, 'S&how More +');

    NewLine;



    self.Width := 125;
    self.Height := 150;
    self.ParentColor := false;
    self.Color := clBtnFace;
    self.AutoSize := true;
    self.BorderWidth := 0;

    self.Padding.top := 2;
    self.Padding.bottom := 2;
    self.Padding.left := 2;
    self.Padding.right := 2;
end;
procedure TClipMenu.CreateParams(var Params: TCreateParams);
begin
    inherited;
    visible := false;

    Params.Style := WS_CHILDWINDOW or WS_CLIPCHILDREN; // WS_CHILDWINDOW doesn't close a WS_POPUP window
    params.ExStyle := {WS_EX_NOACTIVATE or} WS_EX_TOOLWINDOW or WS_EX_TOPMOST or WS_EX_COMPOSITED ;

    if CheckWin32Version(6, 0) then
       params.WindowClass.Style := params.WindowClass.style or CS_DROPSHADOW;

    self.SetPopupClipMode;
end;

procedure TClipMenu.RedoOrder;
var i,j : integer;
begin
    DisableAlign;
    j := 0;
    for i := 0 to controlordercount-1 do begin
        controlorder[i].Top := j;
        controlorder[i].Invalidate;
        inc(j, controlorder[i].Height);
    end;

    // don't care about visibility, the Align will actually take care of this
    EnableAlign;
    self.Height := 10;
end;


procedure TClipMenu.SetACPopupItem(p: TObject);
begin
    {dirty trick to get around circular reference problem}
    acpopupitem := TACPopupItem(p);
    fci := acpopupitem.Clip;

    ShowMoreMode := false;
end;
function TClipMenu.GetACPopupItem : TObject;
begin
    result := TObject(acpopupitem);
end;
procedure TClipMenu.SetPermanentClip(group, index: integer);
begin
    self.isPopup := false;
    self.SetPermanentClipMode;
    fgroup := group;
    findex := index;

    ShowMoreMode := false;
end;
procedure TClipMenu.SetPopupClip(ci : TClipItem);
var i : integer;
begin
    self.isPopup := true;
    self.SetPopupClipMode;
    fci := ci;
end;
procedure TClipMenu.SetPermanentClipMode;
begin
    btnEdit.Enabled := true;
    btnDelete.Enabled := false;
    btnDestroy.Enabled := false;
    btnPaste.Enabled := true;
    btnPasteAs.Enabled := false;
    btnPermanent.Enabled := false;

    btnAsFormat.Visible := false;
    btnPin.Visible := false;
    btnPermanent.Visible := false;
    btnDelete.Visible := false;
    btnDestroy.Visible := false;
    btnPasteAs.Visible := false;
    btnPaste.Visible := true;
end;
procedure TClipMenu.SetPopupClipMode;
begin
    btnEdit.Enabled := true;
    btnDelete.Enabled := true;
    btnDestroy.Enabled := true;
    btnPaste.Enabled := true;
    btnPermanent.Enabled := true;
end;
procedure TClipMenu.SetCustomButton(index: integer; caption, clip : string);
begin
end;
procedure TClipMenu.HandleCustomButton(Sender: TObject);
var i,j : integer;
    s,s2,token : string;
begin
    i := StrToInt(TSpeedButton(Sender).hint);


    // INDEX and STAYOPEN are fake macro commands only used by the Custom Buttons
    //
    s := uppercase( frmconfig.GetCustomData(i) );
    s2 := TokenString(s,'[INDEX=',false);
    if (s<>'') then begin
        //[INDEX=$i]
        token := TokenString(s,']',false);
        s := frmconfig.GetCustomData(i);
        s := StringReplace(s,'[index='+token+']', '',[rfReplaceAll,rfIgnoreCase]);
        s := StringReplace(s,token, IntToStr(acpopupitem.IntegerData),[rfReplaceAll,rfIgnoreCase]);
    end else begin
        s := frmconfig.GetCustomData(i);
    end;


    self.Hide;
    if ContainsText(s,'[STAYOPEN]') then begin
        s := StringReplace(s,'[STAYOPEN]', '',[rfReplaceAll,rfIgnoreCase]);
        FrmMainPopup.SkipFocusReturnOnce;
        CallEventSafe(fOnFormMode, self);
        ACPopupClicks.SendPermanentText(KEYS_STR+s);
        CallEventSafe(fOnFormMode, self);
        CallEventSafe(fOnRemoveClip, self);
    end else begin
        FrmMainPopup.SendMacro(KEYS_STR+s);
    end;
end;


procedure TClipMenu.HandleButtonClick;
begin
    self.Hide;
end;
procedure TClipMenu.HideSubmenus;
begin
    self.DisableAlign; // keeps the correct order when buttons shown again

    btnClipboard.Visible := false;
    btnCtrlV.Visible := false;
    btnShiftInsert.Visible := false;
    btnMimic.Visible := False;
    btnPaste.Visible := false;
    btnPaste.Visible := true;

    btnAsPlain.Visible := false;
    btnAsFormat.Visible := false;
    btnAsFile.Visible := false;
    btnPasteAs.Visible := false;
    btnPasteAs.Visible := true;

    btnPasteAsFile.Visible := false;

    btnAsClip.Visible := false;
    btnAsPlaintext.Visible := false;
    btnPermanent.Visible := False;
    btnPermanent.Visible := True;

    btnPaste.Enabled := true;
    btnPasteAs.Enabled := true;
    btnPermanent.Enabled := true;



    self.EnableAlign;
    self.Height := 50;
end;

procedure TClipMenu.btnPinClick(Sender: Tobject);
var i : integer;
begin
    self.Hide;
    case acpopupitem.ItemType of
    IT_POPUPCLIP: begin
        PinnedClipQueue.InsertAtStart(acpopupitem.clip);
    end;
    IT_PINNED: begin
        PinnedClipQueue.DeleteItem( PinnedClipQueue.IndexOf(acpopupitem.clip));
    end;
    end;
    CallEventSafe(fOnRemoveClip, Self);
end;
procedure TClipMenu.btnDeleteClick(Sender: TObject);
var i : integer;
begin
	case acpopupitem.ItemType of
    IT_CLIPBOARD: begin
        frmClipboardManager.ClearClipboard;
    end;
    IT_POPUPCLIP: begin
        i := ClipQueue.IndexOf(fci);
        if (i <> -1) then begin
            ClipQueue.DeleteItem(i);
        end;
    end;
    end;

    self.Hide;
    CallEventSafe(fOnRemoveClip, self);
end;
procedure TClipMenu.btnDestroyClick(Sender: TObject);
begin
    Windows.SetForegroundWindow(self.Handle);

    if (MessageDlg('Destroy: Permanently delete this item?', mtConfirmation, [mbyes, mbno],0) = mrYes) then begin
        if ClipQueue.IndexOf(fci) <> -1 then begin
            clipqueue.DestroyItem(ClipQueue.IndexOf(self.fci));
            CallEventSafe(fOnDestroyClip, self);
        end;
    end;
end;
procedure TClipMenu.btnEditClick(Sender: TObject);
begin
    CallEventSafe(fOnEditClip, self);
    FrmMainPopup.SkipFocusReturnOnce;
    self.HandleButtonClick;
    
    if (acpopupitem<>nil) and (acpopupitem.ItemType <> IT_PERMANENT) then begin
        if frmConfig.cbEditClipWindow.checked then begin
            FrmEditItem.SetText(fci.GetAsPlaintext, nil);
            FrmMainPopup.ShowPreviewEditForm;
        end else begin
            frmEditTextExternal.EditClip(self.fci);
        end;
    end else begin
//        FrmPermanent.ShowAndSelect(fgroup, findex);
        FrmEditHistory.ShowPermanent(fgroup, findex);
    end;
end;
const EXPANDBUTTON_SHRINK = 4;
procedure TClipMenu.ExpandPasteAsMenu;
var
    x : integer;
begin
    btnPasteAs.Height := btnPasteAs.Height - EXPANDBUTTON_SHRINK;

    btnAsPlain.Visible := false;
    btnAsFile.Visible := true;
    btnAsFormat.Visible := false;
    if not (fci.GetFormatType in [FT_UNICODE, FT_TEXT, FT_PICTURE])  then begin
        btnAsFormat.Visible := true;
        btnAsFormat.Top := btnAsFile.Top + btnAsFile.Height;
        self.Realign;
        case fci.GetFormatType of
        FT_HTML, FT_RICHTEXT: begin
            btnAsPlain.Visible := true;
            btnAsFormat.Caption := SUBMENU_PREFIX + 'Fo&rmatted Text';
        end;
        FT_FILE:
            begin
                btnAsPlain.Visible := true;
                btnAsFormat.Caption := SUBMENU_PREFIX +'Fo&rmat: File(s)';
                btnAsFile.Visible := false;
            end
        else
            btnAsFormat.Caption := SUBMENU_PREFIX +'Fo&rmat';
        end;

    end;
    self.Height := 150; // TODO: Huh?

    btnPasteAs.Enabled := false;
end;
procedure TClipMenu.btnPasteAsClick(Sender: TObject);
begin

    self.AutoSize := false;
    ExpandPasteAsMenu;
    RedoOrder;
    self.AutoSize := true;

    FixPosition;
end;
procedure TClipMenu.AsPlainClick(Sender: TObject);
var s : string;
begin
    if isPopup then begin
        FrmMainPopup.SendPlainText(fci.GetAsPlaintext);
    end else begin
        s := FrmPermanent.GetItemText(findex, fgroup);
        FrmMainPopup.SendPlainText(s);
    end;
end;
procedure TClipMenu.AsFile1Click(Sender: TObject);
var ci : TClipItem;
    s : string;
    procedure PasteClipAsFile(c : TClipItem);
    begin
        if FrmMainPopup.TargetData.DropFilesCompatible then begin
            UnitDropFile.DropFile(c,  FrmMainPopup.TargetData.ForegroundWindow);
        end else begin
            UnitDropFile.DropFile(c);
        end;
    end;
begin
    self.HandleButtonClick;
    CallEventSafe(fOnPasteClip, self);

    if isPopup then begin
        PasteClipAsFile(fci);
    end else begin
        ci := TClipItem.Create;
        TClipDatabase.LoadPermanent(ci, findex, FrmPermanent.PermFoldersGetItem(fgroup));
        PasteClipAsFile(ci);
        myfree(ci);
    end;
end;
procedure TClipMenu.AsFormatClick(Sender: TObject);
begin
    self.HandleButtonClick;
    CallEventSafe(fOnPasteClip, self);
    Paste.SendClip(fci, TRUE);
end;

procedure TClipMenu.btnPasteClick(Sender: TObject);
begin
    self.AutoSize := false;

    btnPaste.Height := btnPaste.Height - EXPANDBUTTON_SHRINK;
    btnMimic.Visible := true;
    btnShiftInsert.Visible := true;
    btnCtrlV.Visible := true;
    btnClipboard.Visible := True;
    btnPaste.Enabled := false;

    RedoOrder;
    self.AutoSize := true;

    FixPosition;
end;
procedure TClipMenu.MimickTyping1Click(Sender: TObject);
var
    s : string;
begin
    self.HandleButtonClick;
    CallEventSafe(fOnPasteClip, self);
//    FrmMainPopup.ShowPopupCallback(nil);  // close the popup

    MySleep(200);
    if isPopup then begin
        Paste.SetKeyboardMimicOnce;
        FrmMainPopup.SendPlainText(fci.GetAsPlaintext);
    end else begin
        s := FrmPermanent.GetItemText(findex,fgroup);

        Paste.SetKeyboardMimicOnce;
        FrmMainPopup.SendPlainText(s);
    end;
end;
procedure TClipMenu.CTRLV1Click(Sender: TObject);
var
    s : string;
begin
    self.HandleButtonClick;
    CallEventSafe(fOnPasteClip, self);

    if isPopup then begin
        Paste.SetUsePasteCVOnce;
        FrmMainPopup.SendClip(fci);
    end else begin
        s := FrmPermanent.GetItemText(findex, fgroup);
        Paste.SetUsePasteCVOnce;
        FrmMainPopup.SendPlainText(s);
    end;
end;
procedure TClipMenu.ShiftInsert1Click(Sender: TObject);
var
    s : string;
begin
    self.HandleButtonClick;
    CallEventSafe(fOnPasteClip, self);

    if isPopup then begin
        Paste.SetUsePasteSIOnce;
        FrmMainPopup.SendClip(fci);
    end else begin
        s := FrmPermanent.GetItemText(findex, fgroup);
        Paste.SetUsePasteSIOnce;
        FrmMainPopup.SendPlainText(s);
    end;
end;
procedure TClipMenu.ClipboardOnly1Click(Sender: TObject);
var
    s : string;
    ci : TClipItem;
begin
    self.HandleButtonClick;
    CallEventSafe(fOnPasteClip, self);

    if isPopup then begin
        ci := fci;
    end else begin
        ci := FrmPermanent.GetItemClip(findex, fgroup);
    end;

    Paste.SetClipboardOnlyOnce;
    FrmMainPopup.SendClip(ci);

    if (not isPopup) then begin
        myfree(ci);
    end;
end;

procedure TClipMenu.btnPermanentClick(Sender: TObject);
var ft : TClipFormatType;
    x : integer;
begin
//    self.HideSubmenus;

    self.AutoSize := false;

    ft := fci.GetFormatType;
    if not (ft = FT_UNICODE) and not (ft = FT_PICTURE) then
        btnAsPlaintext.Visible := true;
    btnAsClip.Visible := true;
    btnAsClip.Caption := SUBMENU_PREFIX+'[No Format Found]';
    case ft of
        FT_UNICODE: begin
        	btnAsClip.Caption := SUBMENU_PREFIX+'&as Unicode';
        end;
        FT_PICTURE: begin
            btnAsClip.Caption := SUBMENU_PREFIX+'&as Picture';
        end;
        FT_HTML: begin;
            btnAsClip.Caption := SUBMENU_PREFIX+'&as HTML';
    	end;
        FT_RICHTEXT: begin
        	btnAsClip.Caption := SUBMENU_PREFIX+'&as Rich Text';
        end;
        FT_FILE: begin
        	btnAsClip.Caption := SUBMENU_PREFIX+'&as Files';
        end;
    end;

    btnPermanent.Enabled := false;
    RedoOrder;

    self.AutoSize := true;

    FixPosition;
end;
procedure TClipMenu.asClip1Click(Sender: TObject);
begin
    CallEventSafe(fOnMakePermanentClip, self);
    self.HandleButtonClick;
    FrmEditHistory.showPermanent(fci);
end;
procedure TClipMenu.asPlaintext1Click(Sender: TObject);
begin
    CallEventSafe(fOnMakePermanentClip, self);
    self.HandleButtonClick;
    frmEditHistory.ShowPermenentAs(fci.GetAsPlaintext);
end;


procedure TClipMenu.btnMoreClick(Sender: TObject);
begin
    if ShowMoreMode then begin
        self.HandleButtonClick;

        FrmConfig.showPage(frmConfig.tsclipmenu);
        FrmConfig.Show;
        TFocusManager.ForceForeground(frmconfig.Handle);
    end else begin
        ShowMoreMode := true;
        self.Hide;
        self.Show;

    end;
end;

procedure TClipMenu.paint;
var
	r : TRect;
begin
	r := self.clientrect;
    canvas.Brush.Style := bsClear;
    canvas.pen.Color := blend(clBtnText,clWhite,60);
    canvas.Rectangle(r);

	//inherited;
end;
procedure TClipMenu.Show;
    procedure SetBold(btn : TSpeedButton);
    var i : integer;
    begin
        btnPin.Font.Style  := btnPin.Font.Style - [fsBold];
        btnEdit.Font.Style  := btnEdit.Font.Style - [fsBold];
        btnDelete.Font.Style  := btnDelete.Font.Style - [fsBold];
        btnDestroy.Font.Style  := btnDestroy.Font.Style - [fsBold];
        if (btn<>nil) and btn.Enabled then begin
            btn.Font.Style  := btn.Font.Style + [fsBold];
        end;

        if btn.Visible then begin
            for i := low(controlorder) to high(controlorder) do begin
                if controlorder[i]=btn then begin
                    controlhover := i;
                    btn.flat := false;
                    BREAK;
                end;
            end;
        end;
    end;
    procedure ConfiguredVisible;
    begin
        btnPin.Visible := (frmconfig.cbClipMenuPin.Checked or ShowMoreMode) and isPopup;
        btnEdit.Visible := frmconfig.cbClipMenuEdit.Checked or ShowMoreMode;
        btnDelete.Visible := frmconfig.cbClipMenuDelete.Checked or ShowMoreMode;
        btnDestroy.Visible := frmconfig.cbClipMenuDestroy.Checked or ShowMoreMode;
        btnConfigure.Visible := isPopup;

        if (not frmconfig.cbClipMenuPasteUsing.Checked) and not showmoremode   then begin
            btnPaste.Visible := false;
            btnMimic.Visible := false;
            btnShiftInsert.Visible := false;
            btnCtrlV.Visible:= false;
            btnClipboard.Visible := false;
        end;
        if not frmconfig.cbClipMenuPasteAs.Checked  and not showmoremode then begin
            btnPasteAs.Visible := false;
            btnAsPlain.Visible := false;
            btnAsFile.Visible := false;
            btnAsFormat.Visible := false;

            btnPasteAsFile.Visible := false;
        end;
        if not frmconfig.cbClipMenuPermanent.Checked  and not showmoremode then begin
            btnPermanent.Visible := false;
            btnAsClip.Visible := false;
            btnAsPlaintext.Visible := false;
        end;

        self.Height := 20;

        if (ShowMoreMode) then begin
            btnConfigure.Caption := '&Configure ...';
        end else begin
            btnConfigure.Caption := 'S&how More +';
        end;
    end;
    procedure MakeFlat;
    var i,shapecnt, wd : integer;
        shape1,shape2 : integer;
    begin
        wd := 0;
        shapecnt := 2; // assume a line may be first
        for i := low(controlorder) to high(controlorder) do begin
            if (controlorder[i] is TShape) then begin
                // detect repeated separator lines and hide them
                controlorder[i].Visible := (shapecnt < 2);
                inc(shapecnt);

                if (controlorder[i].Visible) then begin
                    if (shape1=0) then begin
                        shape1 := i;
                    end else if (shape2=0) then begin
                        shape2 := i;
                    end else begin
                        shape1 := shape2;
                        shape2 := i;
                    end;
                end;
            end else if (controlorder[i] is TSpeedButton) then begin
                with controlorder[i] as TMySpeedButton do begin
                    OnMouseEnter := MouseEnterEvent;
                    Flat := true;
                    wd := max(wd, Canvas.TextWidth(Caption) + Margin);
                    if Visible then begin
                        shapecnt := 0;
                    end;
                end;
            end;
        end;
        if (shapecnt<>0) then begin
            // case where a line is last
            controlorder[shape1].Visible := false;
            controlorder[shape2].Visible := false;
        end;


        self.Width := wd + self.Margins.Left + self.Margins.right + 6;
    end;

    procedure MakeCustomMenus;
    var i, j : integer;
        b : TSpeedButton;
        procedure SetCommon(b : TSpeedButton; caption : string);
            procedure SaveOrder(c : TControl);
            begin
                controlorder[controlordercount] := c;
                inc(controlordercount);
            end;
        begin
            b.Align := alTop;
            b.Layout := blGlyphLeft;
            b.Flat := true;
            b.Margin := 10;
            b.Margins.Top := 0;
            b.Margins.Bottom := 0;
            b.Spacing := 0;
            b.caption := caption;
            b.AlignWithMargins := true;
            b.Height := b.Height - 4;   // Buttons are a bit too tall by default

            self.insertcontrol(b);
            saveorder(b);
        end;
    begin
        if custombuttons=nil then begin
            custombuttons := TList<TSpeedButton>.create;
        end;
        j := 1;
        for i := 0 to FrmConfig.GetCustomCount-1 do begin
            if i >= custombuttons.Count then begin
                btnCustom1 := TMySpeedbutton.Create(self);
                btnCustom1.OnClick := self.HandleCustomButton;
                SetCommon(btnCustom1, FrmConfig.GetCustomCaption(i));
//                btnCustom1.Hint := IntToStr(i);
                btnCustom1.Visible := FrmConfig.GetCustomChecked(i);
                custombuttons.Add(btnCustom1);
            end;
            b := custombuttons[i];
            b.Visible := FrmConfig.GetCustomChecked(i) and isPopup;
            b.Hint := IntToStr(i);
            if b.Visible then begin

                if (j >=1) and (j<=9) then begin
                    b.Caption := '&'+IntToStr(j) + '  ' + frmconfig.GetCustomCaption(i);
                end else begin
                    b.Caption := '   ' + frmconfig.GetCustomCaption(i);
                end;
                inc(j);
            end;

        end;
    end;
begin

    MakeCustomMenus;

    btnPin.Enabled := false;
    btnEdit.Enabled := true;
    btnDelete.Enabled := true;
    btnDestroy.Enabled := true;
    btnPaste.Enabled := true;
    btnPasteAs.Enabled := true;
    btnPermanent.Enabled := true;

    btnPaste.Height := btnPin.Height;
    btnPasteAs.Height := btnPin.Height;
    self.HideSubmenus;

    self.Height := 10;

    if acpopupitem <> nil then begin
        if (acpopupitem.Clip<>nil) and (acpopupitem.Clip.GetFormatType in [FT_HTML, FT_RICHTEXT, FT_FILE]) then begin
            self.ExpandPasteAsMenu;
        end else begin
            if isPopup then begin
                btnPasteAs.Visible := false;
                btnPasteAsFile.Visible := true;
                btnPasteAsFile.Top := btnPasteAs.Top + btnPasteAs.Height;
                btnPermanent.Top := btnPasteAs.Top + btnPasteAs.Height;
            end;
        end;
        case acpopupitem.ItemType of
        IT_TEMP: begin
           btnDestroy.Enabled := false;
        end;
        IT_PINNED: begin
            btnPin.Caption := 'Unpin from &List';
            btnPin.Enabled := true;

            btnEdit.Enabled := true;

            btnDelete.Enabled := false;
            btnDestroy.Enabled := false;
        end;
        IT_POPUPCLIP: begin
            btnPin.Enabled := true;
            btnPin.Caption := 'Pin to &List';
        end;
        IT_PERMANENT:begin
            btnDelete.Enabled := false;
            btnDestroy.Enabled := false;
            btnPermanent.Enabled := false;
        end;
        IT_CLIPBOARD: begin
            btnDestroy.Enabled := false;
        end;
        end;
    end;


    ConfiguredVisible;
    if isPopup then begin
        self.SetPopupClipMode;
    end else begin
        self.SetPermanentClipMode;
    end;

    MakeFlat;
    RedoOrder;
    case FrmConfig.GetIconClickAction of
    CBXICONCLICKACTION_EDIT:
        SetBold(btnEdit);
    CBXICONCLICKACTION_PIN:
        SetBold(btnPin);
    CBXICONCLICKACTION_DELETE:
        SetBold(btnDelete);
    CBXICONCLICKACTION_DESTROY:
        SetBold(btnDestroy);
    CBXICONCLICKACTION_CLIPBOARD:
        SetBold(btnClipboard);
    CBXICONCLICKACTION_MIMIC:
        SetBold(btnMimic);
    CBXICONCLICKACTION_SHIFTINSERT:
        SetBold(btnShiftInsert);
    CBXICONCLICKACTION_CTRLV:
        SetBold(btnCtrlV);
    end;


    Windows.SetWindowPos(self.handle,HWND_TOPMOST,self.left,self.top,self.Width,self.Height,
    	SWP_NOSIZE or SWP_SHOWWINDOW or SWP_NOACTIVATE);
    self.Visible := True;


    {SWP_NOACTIVATE has no effect on focus and did not affect closing the popup}
	Windows.SetFocus(self.Handle);
    fShowing := true;

    // workaround for a partially drawn window
    self.AutoSize := false;
    self.Height := 10;
    self.AutoSize := true;
end;
procedure TClipMenu.Hide;
var i : integer;
begin
    windows.ShowWindow(self.Handle,SW_HIDE);
    self.Visible := false;
    fShowing := false;

//    for i := 0 to custombuttons.Count-1 do begin
//        custombuttons[i].Visible := false;
//    end;
//    self.height := 10;
end;
procedure TClipMenu.MouseEnterEvent(Sender: TObject);
begin
    try
        TSpeedButton(controlorder[controlhover]).Flat := true;
    except
        // ignore if the hover index is invalid
    end;
end;



procedure TClipMenu.FixPosition;
begin
    if self.Left+self.Width > screen.DesktopRect.Right then begin
        self.Left := screen.DesktopRect.Right - self.Width;
    end;
    if self.Top + self.Height > screen.DesktopRect.Bottom then begin
        self.Top := screen.DesktopRect.Bottom - self.Height;
    end;
end;

procedure TClipMenu.KeyDown(var Key: Word; Shift: TShiftState);
begin
  inherited;

end;
procedure TClipMenu.KeyPress(var Key: Char);
begin
  inherited;

end;


end.
