unit UnitFrmDummyClipboardBar;
// NOTES: There's no good place to hook into for loading the state
interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, UnitClipQueue, clipbrd, ExtCtrls, Menus, UnitMisc,
  UnitFrameClipDisplay, Vcl.StdCtrls, Vcl.Buttons,
  Vcl.PlatformDefaultStyleActnCtrls, Vcl.ActnPopup, UnitFrmTooltipNew, UnitFileCatch,
  WinAPI.ActiveX, System.Generics.collections;

type
	TfrmClipboardBar = class(TForm, TDropCallback)
    {$REGION 'DEFAULT'}
		timResize: TTimer;
    pnl1: TPanel;
    imgType: TImage;
    FrameClipDisplay1: TFrameClipDisplay;
    pnl2: TPanel;
    imgClose: TImage;
    timIgnoreClick: TTimer;
    pmSystemMenu: TPopupActionBar;
    timHint: TTimer;
    pnlClient: TPanel;
    pnlToolbar: TPanel;
    btnEdit: TSpeedButton;
    pmPermanent: TPopupActionBar;
    AsText1: TMenuItem;
    AsFormat1: TMenuItem;
    Cancel1: TMenuItem;
    N1: TMenuItem;
    Shape1: TShape;
    pmAlterClip: TPopupActionBar;
    rim1: TMenuItem;
    RemoveDoubleSpaces1: TMenuItem;
    ReplaceDoubleLinefeeds1: TMenuItem;
    ReplacesTabswithSpaces1: TMenuItem;
    Uppercase1: TMenuItem;
    Lowercase1: TMenuItem;
    CapitalizeWords1: TMenuItem;
    Cancel2: TMenuItem;
    N2: TMenuItem;
    Delete1: TMenuItem;
    N3: TMenuItem;
    pnlDropDowns: TPanel;
    btnAlterClip: TSpeedButton;
    btnPermanent: TSpeedButton;
    btnSystem: TSpeedButton;
    pmFont: TPopupActionBar;
    btnFont: TSpeedButton;
    SmallFont1: TMenuItem;
    MediumFont1: TMenuItem;
    LargeFont1: TMenuItem;
    N4: TMenuItem;
    Cancel3: TMenuItem;
    Configuration1: TMenuItem;
    N5: TMenuItem;
    EditHistory1: TMenuItem;
    N6: TMenuItem;
    Flush1: TMenuItem;
    Cancel4: TMenuItem;
    FlushClipboard1: TMenuItem;
    FlushEverything1: TMenuItem;
    FlushPopup1: TMenuItem;
    FlushRemovedClips1: TMenuItem;
    N7: TMenuItem;
    N8: TMenuItem;
    PermanentClipsMacros2: TMenuItem;
    N9: TMenuItem;
    Search1: TMenuItem;
    InverseCase1: TMenuItem;
    CompactWhitespace1: TMenuItem;
    N10: TMenuItem;
    timFadeOut: TTimer;
    RemoveLinefeeds1: TMenuItem;
    procedure FormPaint(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure timResizeTimer(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormShow(Sender: TObject);
    procedure FormHide(Sender: TObject);
    procedure pnl1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure pnl1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure pnl1MouseLeave(Sender: TObject);
    procedure imgCloseClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
//    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
//      Shift: TShiftState; X, Y: Integer);
    procedure pmAlterClick(Sender: TObject);
    procedure imgCloseMouseLeave(Sender: TObject);
    procedure FrameClipDisplay1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure timIgnoreClickTimer(Sender: TObject);
    procedure pnl1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FrameClipDisplay1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FrameClipDisplay1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure FrameClipDisplay1MouseLeave(Sender: TObject);
    procedure FrameClipDisplay1MouseEnter(Sender: TObject);
    procedure timHintTimer(Sender: TObject);
    procedure btnSystemClick(Sender: TObject);
    procedure btnEditClick(Sender: TObject);
    procedure pmPermanentPopup(Sender: TObject);
    procedure btnPermanentClick(Sender: TObject);
    procedure btnMouseEnter(Sender: TObject);
    procedure btnMouseLeave(Sender: TObject);
    procedure btnAlterClipClick(Sender: TObject);
    procedure imgCloseMouseEnter(Sender: TObject);
    procedure Delete1Click(Sender: TObject);
    procedure pnlToolbarMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure AsText1Click(Sender: TObject);
    procedure AsFormat1Click(Sender: TObject);
    procedure btnFontClick(Sender: TObject);
    procedure SmallFont1Click(Sender: TObject);
    procedure MediumFont1Click(Sender: TObject);
    procedure LargeFont1Click(Sender: TObject);
    procedure Configuration1Click(Sender: TObject);
    procedure EditHistory1Click(Sender: TObject);
    procedure FlushClipboard1Click(Sender: TObject);
    procedure FlushPopup1Click(Sender: TObject);
    procedure FlushRemovedClips1Click(Sender: TObject);
    procedure FlushEverything1Click(Sender: TObject);
    procedure PermanentClipsMacros2Click(Sender: TObject);
    procedure Search1Click(Sender: TObject);
    procedure CompactWhitespace1Click(Sender: TObject);
    procedure FormDeactivate(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure timFadeOutTimer(Sender: TObject);
    {$ENDREGION}
  private
    { Private declarations }
    showbar : boolean;
    flasthash : cardinal;
    lastx, lasty,lasttop, lastleft : integer;
    fPopupShowing : boolean;
    fIgnoreMouseLeftOnce : boolean;
    fclipsequence : cardinal;

    fMouseWasDown : boolean;
    fStartPoint : TPoint;
    fDragging : boolean;
    fToolTip : TFrmTooltipNew;
    fHintText : string;
    fHintClip : TClipItem;
    fDropFormat : cardinal;
    fNormalOpacity : byte;
    fHoverOpacity : byte;

    CurrentlyActivated : boolean;
    MouseEnterEvents : TDictionary<TControl, TNotifyEvent>;
    MouseLeaveEvents : TDictionary<TControl, TNotifyEvent>;

    procedure PreMouseEnter(Sender: TObject);
    procedure PreMouseLeave(Sender: TObject);
    procedure CreateParams(var Params: TCreateParams);  override;
    procedure WMResize(var ms : TWMSizing); message WM_SIZING;
    procedure WMMoving(var Msg: TWMMoving); message WM_MOVING;
    procedure WMNCHitTest(VAR Msg: TWMNcHitTest); message WM_NCHITTEST;
    procedure WMWindowPosChanging(var Message: TWMWindowPosChanging); message WM_WINDOWPOSCHANGING;

    procedure DrawClip(forceRedraw : boolean = false);
    procedure IgnoreClicks;
    procedure ClosePopup;


    procedure ShowPopupMenu(pm : TPopupMenu; button : TControl = nil);

    procedure ShowPopup;
    procedure ShowEditClipboard;

    function DropAllowed(const FileNames: array of string): Boolean; overload;
    function DropAllowed(dataObj : IDataObject) : boolean; overload;
    procedure Drop(const FileNames: array of string); overload;
    procedure Drop(dataObj : IDataObject); overload;

    procedure ShowHint(hint : string); overload;
    procedure ShowHint(ci : TClipItem); overload;
    procedure HideHint;

    procedure SetNormalOpacity(value : byte);
    procedure FixPosition;
    procedure FixSize;
    function MenuShowing : boolean;
    procedure CloseAllMenus;
  public
    { Public declarations }
    procedure NewClipFound;
    procedure SetVisible(vis : boolean; MousePosition : boolean = true);
    function IsVisible : boolean;

    procedure GetSizePos(var x: integer; var y : integer; var width : integer;  var height: integer);
    procedure SetSizePos(x,y,width,height : integer);

    procedure ShowToolbar;
    procedure HideToolbar;

    procedure SnapToScreen;

    property NormalOpacity : byte read fNormalOpacity write SetNormalOpacity;
    property HoverOpacity : byte read fHoverOpacity write fHoverOpacity;

    procedure WaitForDeactivate;

  end;

var
	frmClipboardBar: TfrmClipboardBar;

implementation

uses UnitFrmConfig, UnitTWideChar, Math, UnitFrmEditTextExternal,
  UnitKeyboardQuery, UnitFrmClipboardManager, UnitPopupGenerate,
  UnitFrmTriggerWindow, UnitFrmSysTrayMenu, UnitFrmEditItem, UnitDropFile,
  UnitDragClip, System.IOUtils, Vcl.Imaging.pngimage, Vcl.Imaging.jpeg,
  UnitFrmPasteSelected, UnitPaste, UnitFrmPermanentNew, UnitFrmDebug,
  UnitFrmEditHistory, UnitFocusManager;

{$R *.dfm}

// just a hack to get access to OnMouseEnter, OnMouseLeave events
type TMouseEventControl = class(TControl);

{public interface}
procedure TfrmClipboardBar.NewClipFound;
begin
    fclipsequence := WIndows.GetClipboardSequenceNumber;
    if self.showbar then begin
        CurrentClipboard.GetClipboardItem(0);
        self.DrawClip(true);
    end;
end;
procedure TfrmClipboardBar.FixPosition;
begin
    // keep visible, otherwise the right dragbar gets hidden
    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 TfrmClipboardBar.SetVisible(vis: Boolean; MousePosition : boolean = true);
var left, top : integer;
    bt : byte;
begin
    if not vis and not self.Visible then begin
        EXIT;
    end;

    frmConfig.SaveClipboardBarState;


    self.showbar := vis;
    if MousePosition and self.showbar then begin
        self.Top := mouse.CursorPos.Y + 20;
        self.Left := mouse.CursorPos.x + 20;
    end;
    left := self.Left;
    top := self.Top;
    SetWindowLong(
            Handle,
            GWL_STYLE, GetWindowLong( Handle, GWL_STYLE ) and not WS_CAPTION
        );
    SetWindowPos(self.handle, HWND_TOPMOST, self.left,self.top, self.Width, self.Height,
    	SWP_DRAWFRAME or SWP_NOACTIVATE);

	if self.showbar then begin
        //self.Show;
        SetWindowPos(self.handle, HWND_TOPMOST, 0,0, 0, 0, SWP_SHOWWINDOW or SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE or SWP_NOZORDER);
        CurrentClipboard.GetClipboardItem(0);
        self.Visible := true;

        if not frmconfig.cbBarSnapToScreen.Checked then begin
            if self.Left <> left then
                self.Left := left;
            if self.Top <> top then
                self.Top := left;
        end;

        self.FixPosition;
    end else begin
        // when a new window is shown, the clipboard bar gets set visible somehow
        // shifting the foreground window works around this issue
        SetForegroundWindow(FrmMainPopup.Handle);
        self.Visible := false;
    	Windows.ShowWindow(self.handle, SW_HIDE);

    end;


    frmConfig.SaveClipboardBarState(true);

end;
function TfrmClipboardBar.IsVisible: Boolean;
begin
    result := self.showbar;
end;

procedure TfrmClipboardBar.FixSize;
begin
    if self.Height < self.Constraints.MinHeight then
        self.Height := self.Constraints.MinHeight;
    if self.Width < self.Constraints.MinWidth then
        self.Width := self.Constraints.MinWidth;
end;
procedure TfrmClipboardBar.SetSizePos(X,Y, width, height: Integer);
begin
	self.Left := x;
    self.Top := y;
    self.Width := width;
    self.Height := height;

    self.FixSize;
end;
procedure TfrmClipboardBar.GetSizePos(var X: Integer; var Y: Integer; var width: Integer; var height: integer);
begin
	x := self.Left;
    y := self.Top;
    width := self.Width;
    height := self.Height;
end;

procedure TfrmClipboardBar.WaitForDeactivate;
var i : integer;
begin
    i := 0;
    while (GetForegroundWindow = self.Handle) and (i < 15) do begin
        mysleep(50);
        inc(i);
    end;
end;


{property stuff}
procedure TfrmClipboardBar.SetNormalOpacity(value : byte);
begin
    fNormalOpacity := value;
    self.AlphaBlendValue := fNormalOpacity;
end;

{Form Events}
// Visibility Fade
procedure TfrmClipboardBar.PreMouseEnter(Sender: TObject);
var ne : TNotifyEvent;
begin
    if MouseEnterEvents.TryGetValue(TControl(Sender), ne) then begin
        if Assigned(ne) then ne(Sender);
    end;
    self.AlphaBlendValue := fHoverOpacity;
    timFadeOut.Enabled := false;
end;
procedure TfrmClipboardBar.PreMouseLeave(Sender: TObject);
var ne : TNotifyEvent;
begin
    if MouseLeaveEvents.TryGetValue(TControl(Sender), ne) then begin
        if Assigned(ne) then ne(Sender);
    end;
    if not MenuShowing and not KeyboardQuery.IsClicked(leftButton) then
        self.timFadeOut.Enabled := true;
end;
procedure TfrmClipboardBar.timFadeOutTimer(Sender: TObject);
begin
    self.timFadeOut.Enabled := false;
    self.AlphaBlendValue := fNormalOpacity;

     // bad hack, but don't know where to catch the event after a move has finished
    frmConfig.SaveClipboardBarPosition;
end;


procedure TfrmClipboardBar.CreateParams(var Params: TCreateParams);
begin
	inherited;
    params.style := WS_THICKFRAME or WS_SYSMENU and not WS_CAPTION;
	params.ExStyle := WS_EX_TOOLWINDOW or WS_EX_TOPMOST or WS_EX_NOACTIVATE;
// Need NoActivate for showing the popup when clicked
// this, however, breaks dragging in real-time
// Using WM_SIZING WM_MOVING to work around this issue

    params.WndParent := GetDesktopwindow;
    params.WindowClass.Style :=  params.WindowClass.Style or CS_SAVEBITS;
end;
procedure TfrmClipboardBar.FormCreate(Sender: TObject);
    procedure InitToolbar(p : TPanel);
    var
        i : integer;
        c : TControl;
    begin
        for i := 0 to p.ControlCount-1 do begin
            c := p.Controls[i];
            if c is TSpeedButton then begin
                TSpeedButton(c).OnMouseUp := self.pnlToolbarMouseUp;
            end;
            if c is TShape then begin
                TShape(c).OnMouseUp := self.pnlToolbarMouseUp;
            end;
            if c is TPanel then begin
                TPanel(c).OnMouseUp := self.pnlToolbarMouseUp;
                InitToolbar(Tpanel(c));
            end;
        end;
    end;
    procedure InjectMouseEnter;
        procedure ScanChildren(c : TControl);
        var
            i : integer;
            procedure AddHooks(c : TControl);
            begin
                MouseEnterEvents.Add(c, TMouseEventControl(c).OnMouseEnter);
                TMouseEventControl(c).OnMouseEnter := self.PreMouseEnter;

                MouseLeaveEvents.Add(c, TMouseEventControl(c).OnMouseLeave);
                TMouseEventControl(c).OnMouseLeave := self.PreMouseLeave;
            end;
        begin
            if c is TWinControl then begin
                for i := 0 to TWinControl(c).ControlCount - 1 do begin
                    ScanChildren(TWinControl(c).controls[i]);
                end;
            end;

            AddHooks(c);
        end;
    begin
        MouseEnterEvents := TDictionary<TControl, TNotifyEvent>.Create;
        MouseLeaveEvents := TDictionary<TControl, TNotifyEvent>.Create;
        ScanChildren(self);
    end;
begin
    SetWindowLong(
            Handle,
            GWL_STYLE, GetWindowLong( Handle, GWL_STYLE ) and not WS_CAPTION
    );
    self.Visible := false;

    FrameClipDisplay1.Init;
    FrameClipDisplay1.OnMouseUp := FrameClipDisplay1MouseUp;
    FrameClipDisplay1.VisibleEmpty := true;
    FrameClipDisplay1.NoRawHTML := true;

    fToolTip := TFrmTooltipNew.Create(self);
    fToolTip.DodgeMouse := false;
    fToolTIp.MinWidth := 20;


    UnitFileCatch.AddDropListener(self.Handle, self);

    InitToolbar(pnlToolbar);

    fHintText := '';
    fHintClip := nil;

    fNormalOpacity := 100;
    fHoverOpacity := 255;

    InjectMouseEnter;
end;

procedure TfrmClipboardBar.FormDeactivate(Sender: TObject);
begin
    ShowWindow(self.Handle, SW_SHOWNOACTIVATE);
    FrmDebug.AppendLog('CLIPBAR DEACTIVATED');
    CurrentlyActivated := false;
end;
procedure TfrmClipboardBar.FormActivate(Sender: TObject);
begin
    CurrentlyActivated := true;
    ShowWindow(self.Handle, SW_SHOWNOACTIVATE);
    FrmDebug.AppendLog('CLIPBAR ACIVATED');
end;
procedure TfrmClipboardBar.FormClose(Sender: TObject; var Action: TCloseAction);
begin
    Windows.ShowWindow(self.handle, SW_HIDE);
    action := caHide;
end;
procedure TfrmClipboardBar.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
	CanClose := true;
    if not application.Terminated then begin
    	self.showbar := false;
    end;
end;
procedure TfrmClipboardBar.FormHide(Sender: TObject);
begin
    timResize.Enabled := False;
end;
procedure TfrmClipboardBar.FormPaint(Sender: TObject);
begin
    DrawClip;
end;
procedure TfrmClipboardBar.FormShow(Sender: TObject);

begin
    timResize.Enabled := true;

    fToolTip.MaxWidth := FrmConfig.UDToolWidth.Position;
    fToolTIp.MaxHeight := FrmConfig.UDToolHeight.Position;
    fTooltip.DodgeMouse := true;
    fToolTip.DoggeMouseX := false;


    if FrmConfig.cbBarSnapToScreen.Checked then self.SnapToScreen;
end;
procedure TfrmClipboardBar.SnapToScreen;
var r : TRect;
    m : TMonitor;
begin
    m := Screen.PrimaryMonitor;
    if FrmConfig.cbClipboardBarVirtualDesktop.Checked then begin
        r := screen.DesktopRect;
    end else begin
        r := m.WorkareaRect;
    end;

    with r do begin
        // center in rect
        if (frmconfig.btnBarLeft.down or frmconfig.btnBarRight.down) then begin
            self.Top := r.Top + trunc((r.bottom-r.top)/2) - trunc(self.Height/2);
        end;
        if (frmconfig.btnBarTop.Down or frmconfig.btnBarBottom.Down) then begin
             self.Left := trunc((r.Right-r.left)  / 2) - (self.Width div 2);
        end;
        // Top Set
        // Bottom Set
        if (frmconfig.btnBarTop.down or frmconfig.btnBarTopLeft.down or frmconfig.btnBarTopRight.down) then begin
            self.top := r.Top;
        end;
        if (frmconfig.btnBarBottomLeft.down or frmconfig.btnBarBottomRight.down or frmconfig.btnBarBottom.down) then begin
            self.top := r.Top + (r.bottom-r.top) - self.height;
        end;
        // Left Set
        // Right Set
        if (frmconfig.btnBarLeft.down or frmconfig.btnBarTopLeft.down or frmconfig.btnBarBottomLeft.down) then begin
            self.Left := r.Left;
        end;
        if (frmconfig.btnBarRight.down or frmconfig.btnBarTopRight.down or frmconfig.btnBarBottomRight.down) then begin
            self.Left := r.left + (r.right-r.left) - self.Width;
        end;
    end;
end;


{DropTarget functions}
function TfrmClipboardBar.DropAllowed(const FileNames: array of string): Boolean;
var ext : string;
begin
    result := false;
    if fDragging then EXIT;

    result :=(length(filenames) = 1);
    if result then begin
        ext := lowercase(ExtractFileExt(filenames[0]));
        result :=
            (ext = '.txt') or
            (ext = '.rtf') or
            (ext = '.bmp') or
            (ext = '.png') or
            (ext = '.jpg') or (ext = '.jpeg');
    end;
end;
function TfrmClipboardBar.DropAllowed(dataObj : IDataObject) : boolean;
var
    formatetcIn : TFormatEtc;

    e : IEnumFORMATETC;
    i, j : integer;
    x : longInt;
    y :  cardinal;
begin
    result := false;
    if fDragging then EXIT;

    fDropFormat := 0;
    if dataObj.EnumFormatEtc(DATADIR_GET, e) = S_OK then begin
        j := 0;
        y := UnitMisc.GetCF_RICHTEXT;
        repeat
            i := e.Next(1, formatetcIn, @x);
            if (i <> S_OK) then BREAK;
            with formatetcIn do begin
                if (cfFormat=CF_UNICODETEXT) or (cfFormat=y) then begin
                    if (dwAspect = DVASPECT_CONTENT) and
                    ((tymed and TYMED_HGLOBAL)<>0) and
                    (lindex = -1) and (ptd=nil) then begin
                        if (fDropFormat=CF_UNICODETEXT) or not result then begin
                            result := true;
                            fDropFormat := cfFormat;
                        end;
                    end;
                end;
            end;
        until false;
    end;
end;
procedure TfrmClipboardBar.Drop(const FileNames: array of string);
var ext : string;
    ms : TMemoryStream;
    fs : TFileStream;
    frmt : cardinal;

    s : string;
    h : THandle;


//    j : TJpegImage;
    APalette : HPALETTE;
    function GetFilestream : TFileStream;
    begin
        result := TFileStream.Create(Filenames[0], fmOpenRead);
    end;
    procedure HandleGraphic(g : TGraphic);
    var wd : word;
    begin
        g.LoadFromStream(fs);
        g.SaveToClipboardFormat(wd, h, APalette);
        frmt := wd;
        g.Free;
        myfree(fs);
    end;
begin
    ms := TMemoryStream.Create;
    fs := GetFilestream;
    try
        ext := ExtractFileExt(Filenames[0]);
        h := 0;
        if (ext = '.txt') then begin
            frmt := CF_UNICODETEXT;
            fs.Free;
            s := TFile.readalltext(filenames[0]) + #0;
            h := UnitMisc.DupPointerToHandle(@s[1], length(s) * SizeOf(Char) );
        end else if (ext = '.rtf') then begin
            frmt := UnitMisc.GetCF_RICHTEXT;
            ms.CopyFrom(fs, 0);
            fs.Free;
        end else if (ext = '.bmp') then begin
            HandleGraphic(TBitmap.Create);
        end else if (ext = '.png') then begin
            HandleGraphic(TPngImage.Create);
        end else if (ext = '.jpg') or (ext = '.jpeg') then begin
            HandleGraphic(TJPEGImage.Create);
        end;
        ms.Position := 0;
        if Windows.OpenClipboard(self.Handle) then begin
            Windows.EmptyClipboard;
            FrmDebug.AppendLog('SetClipboardData',true);
            if h = 0 then begin
                h := unitMisc.DupStreamToHandle(ms);
            end;
            Windows.SetClipboardData(frmt, h);
            FrmDebug.AppendLog('SetClipboardData',true);
            Windows.CloseClipboard;
        end;
    except
        if fs <> nil then fs.Free;
        ms.Free;
    end;

end;
procedure TfrmClipboardBar.Drop(dataObj : IDataObject);
var
    formatetcIn: TFormatEtc;
    medium: TStgMedium;
    i : integer;
begin
    with formatetcIn do begin
        cfFormat := fDropFormat;
        dwAspect := DVASPECT_CONTENT;
        ptd := nil;
        lIndex := -1;
        tymed := TYMED_HGLOBAL;
    end;
    fillchar(medium, sizeof(medium), #0);

    i := dataObj.GetData(formatetcIn, medium);
    if i = S_OK then begin
        if Windows.OpenClipboard(self.handle) then begin
            Windows.EmptyClipboard;
            Windows.SetClipboardData(fDropFormat, medium.hGlobal);
            if (fDropFormat <> CF_UNICODETEXT) and (fDropFormat <> CF_DIB) then begin
                formatetcIn.cfFormat := CF_UNICODETEXT;
                i := dataObj.GetData(formatetcIn, medium);
                if (i = S_OK) then begin
                    Windows.SetClipboardData(CF_UNICODETEXT, medium.hGlobal);
                end;
            end;
            Windows.CloseClipboard;
        end;
    end;
end;
procedure TfrmClipboardBar.timIgnoreClickTimer(Sender: TObject);
begin
    timIgnoreClick.Enabled := false;
    fIgnoreMouseLeftOnce := false;
end;



procedure TfrmClipboardBar.DrawClip;
    function ImgToBitmap(img : TImage) : TBitmap;
    begin
        result := img.Picture.Bitmap;
    end;
var v : TBitmap;
    defaultsize : integer;
begin
    if (flasthash = CurrentClipboard.CData.Hash) and not ForceRedraw then EXIT;
    fLastHash := CurrentClipboard.CData.Hash;

    imgType.Picture := nil;
	case CurrentClipboard.GetFormatType of
    FT_TEXT, FT_FILE, FT_HTML, FT_PICTURE, FT_RICHTEXT: begin
        v := nil;
        case CurrentClipboard.GetFormatType of
            FT_FILE: 		v:= ImgToBitmap(FrmMainPopup.imgFile);
            FT_PICTURE: 	v:= ImgToBitmap(FrmMainPopup.imgPic);
            FT_HTML:
                v:= ImgToBitmap(FrmMainPopup.imgHTML);
//                v:= ImgToBitmap(FrmMainPopup.imgPic);
            FT_RICHTEXT:	v:= ImgToBitmap(FrmMainPopup.imgRichText);
        end;
        if (v<>nil) then begin
        	imgType.canvas.Draw(0,0,v);
        end;
    end;
    end;

    if assigned(frmconfig) then begin
        with FrameClipDisplay1 do begin
            pnlDisabler.Font := frmconfig.GetFont;

            defaultsize := max(7, pnlDisabler.Font.Size - 2);;
            if smallfont1.Checked then begin
                pnlDisabler.Font.Size := defaultsize - 2;
            end else if MediumFont1.Checked then begin
                pnlDisabler.Font.Size := defaultsize;
            end else if LargeFont1.Checked then begin
                pnlDisabler.Font.Size := defaultsize + 2;
            end;

            OverrideFont(pnlDisabler.font);
        end;
    end;
    with FrameClipDisplay1 do begin
        PicStretched := true;
        retext.Font := pnlDisabler.Font;
        ShowClip(CurrentClipboard);

        retext.SelStart := 0;
        retext.SelLength := length(retext.Text);
        retext.SelAttributes.Size := pnlDisabler.Font.Size;
    end;

    Application.ProcessMessages;
end;

{Resize Clip/Workaround for drag/resize redrawing with WS_EX_NOACTIVATE}
procedure TfrmClipboardBar.FormResize(Sender: TObject);
begin
	self.timResize.Enabled := false;
    self.timResize.Enabled := true;
end;
procedure TfrmClipboardBar.timResizeTimer(Sender: TObject);
begin
    timResize.Enabled := false;
	self.DrawClip(false);
end;
procedure TfrmClipboardBar.WMResize(var ms: TWMSizing);
begin
	timResize.Enabled := false;
    timResize.Enabled := true;

    SetWindowPos(self.Handle, 0, ms.lpRect.left, ms.lpRect.top, ms.lpRect.width, ms.lpRect.Height, SWP_NOZORDER)
end;
procedure TfrmClipboardBar.WMMoving(var Msg: TWMMoving);
begin
    self.AlphaBlendValue := fHoverOpacity;
    timFadeOut.Enabled := false;
    timFadeOut.enabled := true;

    SetWindowPos(self.Handle, 0, msg.DragRect.left, msg.DragRect.top, msg.DragRect.width, msg.DragRect.Height, SWP_NOZORDER)
end;
procedure TfrmClipboardBar.WMNCHitTest(VAR Msg: TWMNcHitTest);
var
	pt : TPoint;
    procedure MakeHovered;
    begin
    end;
const CORNER_LEN = 5;
begin
	inherited;

    msg.result := HTCLIENT;
    pt := point(msg.XPos, msg.YPos);
    pt := self.ScreenToClient(pt);

    if pt.X < self.ClientRect.Left then begin
        MakeHovered;
        msg.Result := HTLEFT;
        if  (pt.y < self.ClientRect.Top+CORNER_LEN) then begin
            msg.Result := HTTOPLEFT;
        end else if (pt.y > self.ClientRect.Bottom-CORNER_LEN) then begin
            msg.result := HTBOTTOMLEFT;
        end;
    end else if pt.X > self.ClientRect.Right then begin
        MakeHovered;
        msg.Result := HTRIGHT;
        if  (pt.y < self.ClientRect.Top+CORNER_LEN) then begin
            msg.Result := HTTOPRIGHT;
        end else if (pt.y > self.ClientRect.Bottom-CORNER_LEN) then begin
            msg.result := HTBOTTOMRIGHT;
        end;
    end else if pt.Y < self.ClientRect.Top then begin
        MakeHovered;
        msg.Result := HTTOP;
        if  pt.x < self.ClientRect.left+CORNER_LEN then begin
            msg.Result := HTTOPLEFT;
        end else if pt.x > self.ClientRect.right-CORNER_LEN then begin
            msg.result := HTTOPRIGHT;
        end;
    end else if pt.Y > self.ClientRect.Bottom then begin
        MakeHovered;
        msg.Result := HTBOTTOM;
        if  pt.x < self.ClientRect.left+CORNER_LEN then begin
            msg.Result := HTBOTTOMLEFT;
        end else if pt.x > self.ClientRect.right-CORNER_LEN then begin
            msg.result := HTBOTTOMRIGHT;
        end;
    end;
end;
procedure TfrmClipboardBar.WMWindowPosChanging(var Message: TWMWindowPosChanging);
begin
end;
procedure TfrmClipboardBar.IgnoreClicks;
begin
    self.fIgnoreMouseLeftOnce := true;
    self.timIgnoreClick.Enabled := true;
end;

{Hint System}
procedure TfrmClipboardBar.ShowHint(ci : TClipItem);
begin
    fTooltip.Hide;
    fHintClip := ci;;
    timHint.Enabled := true;
end;
procedure TfrmClipboardBar.ShowHint(hint : string);
begin
    fTooltip.Hide;
    fHintText := hint;
    fHintClip := nil;
    timHint.Enabled := true;
end;
procedure TfrmClipboardBar.HideHint;
begin
    timHint.Enabled := false;
    fToolTip.Hide;
end;
procedure TfrmClipboardBar.timHintTimer(Sender: TObject);
begin
    if (not fToolTip.Showing) and
        (not MenuShowing)
     then  begin
        if fHintClip <> nil then begin
            fToolTip.DodgeMouse := true;
            fToolTip.ShowTooltip(fHintClip, Point(self.Left, self.Top + self.Height));
        end else begin
            fToolTIp.SmallFontOnce := true;
            fToolTip.DodgeMouse := false;
            fToolTip.ShowTooltip(fHintText, Point(self.Left, self.Top + self.Height));
        end;
        fHintClip := nil;
    end;
end;
procedure TfrmClipboardBar.FrameClipDisplay1MouseEnter(Sender: TObject);
begin
    if not KeyboardQuery.IsClicked(leftButton) then begin
        if frmconfig.cbShowTooltipOnHover.Checked then begin
            self.ShowHint(CurrentClipboard);;
        end else begin
            self.ShowHint('Click-Right Click    Drag-Drop');
        end;
    end;
end;
procedure TfrmClipboardBar.FrameClipDisplay1MouseLeave(Sender: TObject);
begin
    self.HideHint;
end;

procedure TfrmClipboardBar.btnMouseEnter(Sender: TObject);
var sb : TSpeedButton absolute Sender;
begin
    self.ShowHint(TControl(sender).Hint);
{    if MenuShowing then begin
        Self.CloseAllMenus;
        if (sender = btnAlterClip) and not pmAlterClip.MenuActive then begin
            self.ShowPopupMenu(pmAlterClip, TButton(sender));
        end else if (sender = btnPermanent) and not pmPermanent.MenuActive then begin
            self.ShowPopupMenu(pmPermanent, TButton(sender));
        end else if (sender = btnSystem) and not pmSystemMenu.MenuActive then begin
            self.ShowPopupMenu(pmSystemMenu, TButton(sender));
        end else if (sender = btnFont) and not pmFont.MenuActive then begin
            self.ShowPopupMenu(pmFont, TButton(sender));
        end;
    end;
}
// TODO: find out if this could work smoothly
end;
procedure TfrmClipboardBar.btnMouseLeave(Sender: TObject);
begin
    self.HideHint;
end;

{Toolbar}
procedure TfrmClipboardBar.ShowToolbar;
var useOldPos : boolean;
begin
    if pnlToolbar.Visible then begin
        useOldPos := (self.Top = self.lasttop) and (self.Left = self.lastleft);
        self.HideToolbar;
        if useOldPos then begin
            self.Left := self.lastx;
            self.Top := self.lasty;
        end;
    end else begin
        pnlToolbar.Visible := true;
        pnlToolbar.Top := 0;
        self.ClientHeight := self.ClientHeight + pnlToolbar.Height;

        self.lastx := self.Left;
        self.lasty := self.Top;

        self.FixPosition;
    end;

    self.lasttop := self.Top;
    self.lastleft := self.Left;
end;
procedure TfrmClipboardBar.HideToolbar;
begin
    pnlToolbar.Visible := false;
    self.ClientHeight := self.ClientHeight - pnlToolbar.Height;
end;
procedure TfrmClipboardBar.pnlToolbarMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
    if button = mbRight then
        self.ShowToolbar;
end;

{Toolbar Buttons}
procedure TfrmClipboardBar.ShowPopupMenu(pm : TPopupMenu; button : TControl = nil);
var
    h : THandle;
begin
    timHint.Enabled := false;
    timFadeOut.Enabled := false;
    if MenuShowing then CloseAllMenus;

    h := GetForegroundWindow;
    TFocusManager.ForceForeground(Application.Handle);
    Application.ProcessMessages;
    if TControl <> nil then begin
        UnitMisc.ShowPopupBottom(button, pm);
    end else begin
        pm.Popup(mouse.CursorPos.X+5, mouse.CursorPos.y+5);
    end;

    if not (
        KeyboardQuery.IsClicked(leftButton) and
        PtInRect(self.BoundsRect, mouse.CursorPos)
    ) then begin
        if GetForegroundWindow = self.Handle then
            TFocusManager.ForceForeground(h);
        timFadeOut.Enabled := true;
    end;
//    self.IgnoreClicks;
// TODO: Figure out why clicks were set to ignore here
end;
procedure TfrmClipboardBar.btnAlterClipClick(Sender: TObject);
begin
    self.ShowPopupMenu(pmAlterClip, tcontrol(sender));
end;
procedure TfrmClipboardBar.btnPermanentClick(Sender: TObject);
begin
    self.ShowPopupMenu(pmPermanent, tcontrol(sender));
end;
procedure TfrmClipboardBar.btnSystemClick(Sender: TObject);
begin
//    Self.ShowSystemMenu;
    self.ShowPopupMenu(pmSystemMenu, tcontrol(sender));
end;
procedure TfrmClipboardBar.btnEditClick(Sender: TObject);
begin
    frmEditTextExternal.EditClipboard;
end;
procedure TfrmClipboardBar.btnFontClick(Sender: TObject);
begin
    self.ShowPopupMenu(pmFont, tcontrol(sender));
end;

{Toolbar Popup Menus}
function TfrmClipboardBar.MenuShowing : boolean;
begin
    result := pmSystemMenu.MenuActive or pmPermanent.MenuActive or
        pmAlterClip.MenuActive or pmFont.MenuActive;
end;
procedure TfrmClipboardBar.CloseAllMenus;
begin
    pmSystemMenu.CloseMenu;
    pmPermanent.CloseMenu;
    pmAlterClip.CloseMenu;
    pmFont.CloseMenu;
end;
procedure TfrmClipboardBar.pmPermanentPopup(Sender: TObject);
begin
    AsText1.Enabled := true;
    case CurrentClipboard.GetFormatType of
    FT_PICTURE:
        begin
            AsText1.Enabled := false;
            AsFormat1.Enabled := true;
            AsFormat1.Caption := 'As Picture (DIB)';
        end;
    FT_UNICODE:
        begin
            AsFormat1.Enabled := false;
            AsFormat1.Caption := 'Make Permanent As Format';
        end;
    FT_HTML, FT_RICHTEXT:
        begin
            AsFormat1.Enabled := true;
            AsFormat1.Caption := 'Make Permanent As ' + CurrentClipboard.GetFormatName;
        end;
    FT_UNKNOWN:
        begin
            AsText1.enabled := false;
            AsFormat1.Enabled := false;
            AsFormat1.Caption := 'Make Permanent As Unknown';
        end;
    else
        begin
            AsText1.enabled := false;
            AsFormat1.Enabled := true;
            AsFormat1.Caption := 'Make Permanent As ' + CurrentClipboard.GetFormatName;
        end;
    end;
end;
// Edit
procedure TfrmClipboardBar.ShowEditClipboard;
begin
    frmEditTextExternal.EditClipboard;
end;
// Alter Clip
procedure TfrmClipboardBar.pmAlterClick(Sender: TObject);
begin
    Paste.SendMacro(KEYS_STR+TMenuItem(Sender).hint);
end;
procedure TfrmClipboardBar.Delete1Click(Sender: TObject);
begin
    frmClipboardManager.ClearClipboard;
end;
// Permanent
procedure TfrmClipboardBar.AsFormat1Click(Sender: TObject);
begin
    FrmEditHistory.showPermanent(CurrentClipboard);
end;
procedure TfrmClipboardBar.AsText1Click(Sender: TObject);
begin
    frmEditHistory.ShowPermenentAs(CurrentClipboard.GetAsPlaintext);
end;
procedure TfrmClipboardBar.PermanentClipsMacros2Click(Sender: TObject);
begin
    frmSysTrayMenu.PermanentClipsMenuItemClickEvent(self);
end;
// Font
procedure TfrmClipboardBar.SmallFont1Click(Sender: TObject);
begin
    self.DrawClip(true);
end;
procedure TfrmClipboardBar.LargeFont1Click(Sender: TObject);
begin
    self.DrawClip(true);
end;
procedure TfrmClipboardBar.MediumFont1Click(Sender: TObject);
begin
    self.DrawClip(true);
end;
procedure TfrmClipboardBar.CompactWhitespace1Click(Sender: TObject);
begin
    FrameClipDisplay1.CompaceWhitespace := CompactWhitespace1.Checked;
    self.DrawClip(true);
end;
// System
procedure TfrmClipboardBar.Configuration1Click(Sender: TObject);
begin
     frmconfig.pcPanels.ActivePageIndex := frmconfig.tsClipboardBar.PageIndex;
     frmSysTrayMenu.ConfigureMenuItemClickEvent(self);
end;
procedure TfrmClipboardBar.Search1Click(Sender: TObject);
begin
    FrmMainPopup.SearchMenuItemClickEvent(self);
end;
procedure TfrmClipboardBar.EditHistory1Click(Sender: TObject);
begin
    frmSysTrayMenu.EditHistoryMenuItemClickEvent(self);
end;
procedure TfrmClipboardBar.FlushClipboard1Click(Sender: TObject);
begin
    frmClipboardManager.ClearClipboard;
    self.DrawClip(true);
end;
procedure TfrmClipboardBar.FlushEverything1Click(Sender: TObject);
begin
    frmSysTrayMenu.FlushEverything(self);
    self.DrawClip(true);
end;
procedure TfrmClipboardBar.FlushPopup1Click(Sender: TObject);
begin
    frmSysTrayMenu.FlushPopupClipsClick(self);
    self.DrawClip(true);
end;
procedure TfrmClipboardBar.FlushRemovedClips1Click(Sender: TObject);
begin
    frmSysTrayMenu.FlushRemovedClips(self);
    self.DrawClip(true);
end;

{Clicks and Drags}
procedure TfrmClipboardBar.FrameClipDisplay1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var h : thandle;
begin
    fMouseWasDown := true;
    fStartPoint.X := x;
    fStartPoint.Y := y;

    if GetForegroundWindow = self.Handle then begin
        h := GetTopWindow(0);
        h := GetNextWindow(h);
        h := GetNextWindow(h);
        h := GetNextWindow(h);
        h := GetNextWindow(h);
        TFocusManager.ForceForeground(h);
    end;

end;
procedure TfrmClipboardBar.FrameClipDisplay1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
const THRESHOLD = 5;
begin
    if fMouseWasDown then begin
        if not fDragging then begin
            if (abs(x-fStartPoint.X)> THRESHOLD) or (abs(y-fStartPoint.Y)> THRESHOLD) then begin
                self.IgnoreClicks;
                fDragging := true;
                if UnitDragClip.Execute(CurrentClipboard) <> drDropped then begin
                    FrmDebug.AppendLog('ClipBar - Drag Failed');
                end;
                fDragging := false;
                fMouseWasDown := false;
            end;
        end;
    end;
end;
procedure TfrmClipboardBar.FrameClipDisplay1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
    if not fMouseWasDown then begin
        EXIT;
    end;
    fMouseWasDown := false;
    if pmSystemMenu.MenuActive or pmPermanent.MenuActive then  begin
        self.ClosePopup;
        EXIT;
    end;
    case button of
    mbLeft:
        begin
            if not fIgnoreMouseLeftOnce then begin
                if FrmMainPopup.MainPopup.Showing then begin
                    FrmMainPopup.MainPopup.hide;
                end else begin
                    if FrmConfig.cbShowPopupWhenClicked.Checked then begin
                        WaitForDeactivate;
                        self.ShowPopup;
                    end;
                end;
            end;
        end;
    mbRight:
        begin
            self.ShowToolbar;
        end;
    end;
end;

{Click Actions}
procedure TfrmClipboardBar.ShowPopup;
begin
    FrmMainPopup.ShowOnScratchPad;
end;
procedure TfrmClipboardBar.ClosePopup;
begin
    PostMessage(Handle, WM_LBUTTONDOWN, MK_LBUTTON, 0);
    PostMessage(Handle, WM_LBUTTONUP, MK_LBUTTON, 0);
end;





{close button w/ mouse cursor}
procedure TfrmClipboardBar.imgCloseClick(Sender: TObject);
begin
    self.SetVisible(false);
end;
procedure TfrmClipboardBar.imgCloseMouseEnter(Sender: TObject);
begin
    screen.Cursor := crHandPoint;
end;
procedure TfrmClipboardBar.imgCloseMouseLeave(Sender: TObject);
begin
    screen.Cursor := crDefault;
end;

{dragable panel w/ mouse cursor}
procedure TfrmClipboardBar.pnl1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
const
    SC_DRAGMOVE = $F012;
begin
    self.IgnoreClicks;

    ReleaseCapture;
    Perform( wm_SysCommand, SC_MOVE or HTCAPTION, 0 );
end;
procedure TfrmClipboardBar.pnl1MouseLeave(Sender: TObject);
begin
    screen.Cursor := crDefault;
end;
procedure TfrmClipboardBar.pnl1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
    screen.Cursor := crSizeAll;
end;
procedure TfrmClipboardBar.pnl1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
    self.IgnoreClicks;

end;

end.
