unit UnitFrmRemoved;

{
    Purpose:
        User interface to copy a removed item to the clipboard

    Updates:
        fixed broken "Copy" button
    --------------
        Made form scalable

    NOTES: I'm using the same dirty trick to work-around Win9x control
    memory limitations as I used in FrmClipboardManager.
}

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, UnitClipQueue, ComCtrls, ExtCtrls, {xpman,} Menus, Buttons,
  ImgList;

type
  TFrmRemoved = class(TForm)
    bCopy: TButton;
    bClose: TButton;
    Label1: TLabel;
    mPreview: TMemo;
    bFlushItems: TButton;
    btnCopyAsx: TButton;
    iPreview: TImage;
    lbList: TListBox;
    Label2: TLabel;
    rPreview: TRichEdit;
    FindDialog1: TFindDialog;
    btnFind: TButton;
    btnEdit: TButton;
    btnCopyAs: TSpeedButton;
    pmCopyAs: TPopupMenu;
    CopytoClipboard1: TMenuItem;
    CopyasFormat1: TMenuItem;
    btnPermanent: TSpeedButton;
    pmMore: TPopupMenu;
    CreatePlainText: TMenuItem;
    CreateClip: TMenuItem;
    cbxFilter: TComboBoxEx;
    ilFilter: TImageList;
    btnConfig: TSpeedButton;
    procedure FormShow(Sender: TObject);
    procedure lbListClick(Sender: TObject);
    procedure bCopyClick(Sender: TObject);
    procedure bCloseClick(Sender: TObject);
    procedure bFlushItemsClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormHide(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure btnCopyAsxClick(Sender: TObject);
    procedure lbListMeasureItem(Control: TWinControl; Index: Integer;
      var Height: Integer);
    procedure lbListDrawItem(Control: TWinControl; Index: Integer;
      Rect: TRect; State: TOwnerDrawState);

    procedure FreeRemoved(ci : TClipItem);
    procedure FormKeyUp(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure btnFindClick(Sender: TObject);
    procedure FindDialog1Find(Sender: TObject);
    procedure btnEditClick(Sender: TObject);
    procedure btnCopyAsClick(Sender: TObject);
    procedure btnPermanentClick(Sender: TObject);
    procedure CreatePlainTextClick(Sender: TObject);
    procedure CreateClipClick(Sender: TObject);
    procedure FindDialog1Show(Sender: TObject);
    procedure CopytoClipboard1Click(Sender: TObject);
    procedure CopyasFormat1Click(Sender: TObject);
    procedure cbxFilterDropDown(Sender: TObject);
    procedure cbxFilterChange(Sender: TObject);
    procedure btnConfigClick(Sender: TObject);
  private
    { Private declarations }
    //HistoryLoaded : boolean;
    ci : TClipItem;
     procedure ResetButtons;
     function ClickedClip : TClipItem;
  protected
        procedure CreateParams(var Params: TCreateParams); override;
  public
    { Public declarations }
  end;

var
  FrmRemoved: TFrmRemoved;

implementation

{$R *.dfm}

Uses StrUtils, Clipbrd, UnitFrmHotKey, UnitFrmConfig,
  UnitFrmClipboardManager, UnitPaste, Math, UnitMisc, UnitFrmEditItem,
  UnitFrmEditTextExternal, UnitToken, UnitFrmPermanentNew, INIFiles;
const REMOVED_FILE = 'removed.txt';

procedure TFrmRemoved.FormShow(Sender: TObject);
var i : longint;
    s : string;
begin
    //bCopy.Enabled := false;
    //btnCopyAs.Visible := false;
    //btnEdit.Visible := false;

    mPreview.Clear;

    lbList.Items.BeginUpdate;
    lbList.Items.Clear;
    for i := (RemovedQueue.GetQueueCount - 1) downto 0 do begin
        s := RemovedQueue.GetItemText(i);
        lbList.Items.Add(LeftStr(s,160));
    end;
    lbList.Items.EndUpdate;

    self.ResetButtons;


end;


procedure TFrmRemoved.ResetButtons;
var i : integer;
begin
    btnEdit.Enabled := false;
    bCopy.Enabled := false;
    btnCopyAs.Enabled := false;
    btnFind.Enabled := true;
    btnPermanent.Enabled := false;

    if lblist.itemindex <> -1 then begin
        btnEdit.Enabled := true;
        bCopy.Enabled := true;
        btnCopyAs.Enabled := true;
        btnPermanent.Enabled := true;

        self.FreeRemoved(ci);
        i := (lbList.Count - 1) - lbList.ItemIndex;
        ci := RemovedQueue.GetItemClip(i);

        CopyasFormat1.Caption := 'Copy as ' + ci.GetFormatName(false);
        CreateClip.Caption := 'Create as ' + ci.GetFOrmatName(false) + ' Clip';
    end;
end;

procedure TFrmRemoved.btnConfigClick(Sender: TObject);
begin
    frmconfig.pcPanels.ActivePageIndex := frmconfig.tsHistory.PageIndex;
    frmconfig.Show;
end;

procedure TFrmRemoved.lbListClick(Sender: TObject);
var i : integer;
    s : string;
begin
    self.ResetButtons;

    // reverse the index from 0 - (N-1) to (N-1) - 0
    //bCopy.Enabled := true;
    mPreview.Visible := false;
    iPreview.Visible := false;
    rPreview.Visible := false;

    //bCopy.Enabled := true;
    i := (lbList.Count - 1) - lbList.ItemIndex;
    mPreview.Text := Leftstr(
        RemovedQueue.GetItemText(i),
        10000);

    self.FreeRemoved(ci);
    ci := RemovedQueue.GetItemClip(i);

    //btnCopyAs.Visible := false;
    if (ci <> nil) then begin
        if (ci.GetFormat <> 0) then begin
            //btnCopyAs.Hint := ci.GetFormatName;
            //btnCopyAs.Caption := 'Copy as ' + LeftStr(btnCopyAs.Hint, 10);

            //btnCopyAs.Visible := true;
        end;

        //btnEdit.Visible := mPreview.Text <> '';

        if (ci.GetFormat = CF_DIB) then begin
            mPreview.Visible := false;
            iPreview.Visible := true;
            ci.GetDIB(iPreview.Picture);
        end;
        if (ci.GetFormat = UnitMisc.GetCF_RICHTEXT) then begin
            mPreview.Visible := false;
            iPreview.Visible := false;
            rPreview.Visible := true;
            ci.GetRichText(s);
            rPreview.Text := s;
        end;
    end;

    if(not iPreview.Visible) and (not rPreview.Visible)then begin
        mPreview.Visible := true;
    end;
end;

procedure TFrmRemoved.bCopyClick(Sender: TObject);
var i : integer;
begin


    Paste.SetClipboardOnlyOnce;
    i := (lbList.Count - 1) - lbList.ItemIndex;

    // to be more inuitive, let any copied removed item back onto the popup

    //if frmconfig.cbMoveFirst.checked then begin
        frmClipboardManager.DisablePasteProtectionOnce;
    //end;

    Paste.SendText(RemovedQueue.GetItemText(i), nil);
    
    self.hide;
end;

procedure TFrmRemoved.bCloseClick(Sender: TObject);
begin
    lblist.Items.Clear;
    self.Close;
end;

procedure TFrmRemoved.bFlushItemsClick(Sender: TObject);
begin
    if (MessageDlg('Delete all Removed Clips?', mtConfirmation, [mbyes, mbno],0) = mrYes) then begin
        RemovedQueue.ClearQueue;
        lbList.Items.Clear;
        self.mPreview.Clear;
        self.rPreview.Clear;
        self.iPreview.Visible := false;
        self.rPreview.Visible := false;

        self.FormShow(self);
    end;
end;


procedure TFrmRemoved.FormClose(Sender: TObject; var Action: TCloseAction);
begin
    lblist.Items.Clear;
    mPreview.Clear;

    {
    if FrmConfig.cbRemember.checked then begin
        frmClipboardManager.SaveHistory;
    end;
    }
end;

procedure TFrmRemoved.FormHide(Sender: TObject);
begin
    lbList.Items.Clear;
end;

procedure TFrmRemoved.FormCreate(Sender: TObject);
begin
    UnitMisc.AppendLog('FrmRemoved - creating');

    //ci := TClipItem.Create;
    //lblist.style

    // lbOwnerDrawVariable is buggy with smooth scrolling
    // this is a workaround so it smoothscrolls in the right direction but
    // allows for a larger height item
    lblist.IntegralHeight := false;
    lblist.ItemHeight := math.max(17, lbList.Canvas.TextHeight('ALKJDIOJ')+3);
end;

procedure TFrmRemoved.btnCopyAsClick(Sender: TObject);
var p : TPoint;
begin
    p := btnCopyAs.ClientToScreen(point(btnCopyAs.Width,0) );
    pmCopyas.Popup(p.X, p.y);
end;

procedure TFrmRemoved.btnCopyAsxClick(Sender: TObject);
begin
    Paste.SetClipboardOnlyOnce;
    Paste.SendText('', ci);
end;

procedure TFrmRemoved.lbListMeasureItem(Control: TWinControl;
  Index: Integer; var Height: Integer);
begin
  height := math.max(16, lbList.Canvas.TextHeight('ALKJDIOJ')+4);
end;



procedure TFrmRemoved.lbListDrawItem(Control: TWinControl; Index: Integer;
  Rect: TRect; State: TOwnerDrawState);
var ci : TClipItem;
    th, h : integer;
    c : TColor;
    header, s, s2 : string;
    dt : TDateTime;
const HEADER_WIDTH = 120;
begin
    // height of rectangle & height of text on the listbox
    h := (rect.Bottom - rect.top) + 1;
    th := lbList.Canvas.TextHeight('ALKJDFIOJ');


    //
    // draw the icon leaving enough pixels above and below
    // for the selection outline
    //
    s := lbList.Items.Strings[index];

    ci := RemovedQueue.GetItemClip((lbList.Count - 1) - index);
    dt := RemovedQueue.GetItemDate((lbList.Count - 1) - index);
    if (ci <> nil) then begin
        DrawIconEX(
            lbList.Canvas.Handle, rect.Left+2, rect.Top+1,ci.CData.GetHICON,
            h-2, h-2,
            0,0,DI_NORMAL);

        DateTimeToString(s2, 'mm-dd-yy', dt);
        //s := UnitMisc.GetCliptypeSymbol(ci.GetFormat) + ' ' + s2 + ':   ' + s;
        //s := s2 + ' ['+ ci.GetFormatName(false) +']:  ' + s;

        header := s2 + ' ['+ ci.GetFormatName(false) +']:  ' ;

        self.FreeRemoved(ci);
    end;

    // leave a space for the icon
    // center the text if possible
    if (h  <=  th) then begin
        lbList.Canvas.TextOut(
            rect.Left + h + 3, rect.Top, header
        );
        lbList.Canvas.TextOut(
            rect.Left + h + 3 + HEADER_WIDTH, rect.Top, s
        );

    end else begin
        lbList.Canvas.TextOut(
            rect.Left + h + 3, floor((h-th)/2) + rect.Top, header
        );
        lbList.Canvas.TextOut(
            rect.Left + h + 3 +HEADER_WIDTH, floor((h-th)/2) + rect.Top, s
        );
    end;


    //
    // Draw/Clear the selection outline
    //
    c := lbList.Canvas.Brush.color;
    if (odSelected in state) then begin
        lbList.Canvas.Brush.Color := clHighlight;
        lbList.Canvas.FrameRect(rect);
    end else begin
        lbList.Canvas.Brush.color := clWindow;
        lbList.Canvas.FrameRect(rect);
    end;
    lbList.Canvas.Brush.color := c;

end;
procedure TFrmRemoved.FreeRemoved(ci: TClipItem);
var h : HICON;
begin
    //
    // Removed items are generated on the fly and resources must
    // be freed manually.
    //
    if (ci <> nil) then begin
        if (ci.CData <> nil) then begin
            h := ci.CData.GetHICONAbsolute;
            UnitMisc.MyDestroyIcon(h);
        end;
        MyFree(ci);
    end;
end;

procedure TFrmRemoved.FormKeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
    if (KEY = VK_ESCAPE) then begin
        self.bClose.Click;
    end;
end;

procedure TFrmRemoved.btnFindClick(Sender: TObject);
//var y : integer;
begin
    {
    y := mouse.CursorPos.Y;
    FindDialog1.Position := point(FindDIalog1.Position.x, y);
    }
    FindDialog1.Execute; 
end;

procedure TFrmRemoved.btnPermanentClick(Sender: TObject);
var p : TPoint;
begin
    self.FreeRemoved(ci);
    ci := self.ClickedClip;
    if (ci <> nil) then begin
        CreateClip.Caption := 'Create ' + ci.GetFormatName(false) + ' Clip';

        p := btnPermanent.ClientToScreen(point(btnPermanent.Width,0) );
        pmMore.Popup(p.X, p.y);

        ResetButtons;
    end;
end;

function TFrmRemoved.ClickedClip: TClipItem;
var i : integer;
begin
    result := nil;
    if lblist.ItemIndex = -1 then EXIT;

    i := (lbList.Count - 1) - lbList.ItemIndex;
    result := RemovedQueue.GetItemClip(i);
end;

procedure TFrmRemoved.cbxFilterChange(Sender: TObject);
var cbx : TComboExItem;
    sl : TStringList;
    i : integer;
begin
    cbx := cbxfilter.ItemsEx.ComboItems[cbxfilter.ItemIndex];
    sl :=  TStringList(cbx.data);
    lblist.ClearSelection;
    lblist.MultiSelect := true;
    for i := 0 to sl.count - 1 do begin
        lbList.Selected[ (lbList.Count - 1) - StrToInt(sl[i]) ] := true;
    end;
    //lblist.MultiSelect := false;
end;

procedure TFrmRemoved.cbxFilterDropDown(Sender: TObject);
var i, j,  index : integer;
    ci : TClipItem;
    sh : TStringHash;
    icon : TIcon;
    c : cardinal;
    cbx : TComboExItem;
begin

    sh := TStringHash.Create(2048);
    cbxFilter.ItemsEx.Clear;

    for i := 0 to RemovedQueue.GetQueueCount- 1 do begin
        icon := TIcon.Create;
        icon.Handle := RemovedQueue.GetItemIcon(i);
        c := unitmisc.IconCRC(icon.handle);

        j := sh.ValueOf(IntToStr(c));
        if j = -1 then begin


            index := ilFilter.AddIcon(icon);

            cbx := cbxFilter.ItemsEx.Add;
            cbx.ImageIndex := index;
            cbx.Caption := 'program';
            cbx.data := Pointer(TStringList.Create);
            TStringList(cbx.Data).Add(IntToStr(i));

            sh.Add(IntToStr(c), cbx.index);

        end else begin
            cbx := cbxfilter.ItemsEx.ComboItems[j];
            TStringList(cbx.Data).Add(IntToStr(i));
        end;

        DestroyIcon(icon.handle);
        myfree(icon);
    end;

    myfree(sh);
end;

procedure TFrmRemoved.CopyasFormat1Click(Sender: TObject);
begin
    self.FreeRemoved(ci);
    ci := self.ClickedClip;
    if (ci = nil) then EXIT;
    
    Paste.SetClipboardOnlyOnce;
    frmClipboardManager.DisablePasteProtectionOnce;
    Paste.SendText('', ci);
end;

procedure TFrmRemoved.CopytoClipboard1Click(Sender: TObject);
begin
    bCopy.click;
end;

procedure TFrmRemoved.CreateClipClick(Sender: TObject);
begin
    self.FreeRemoved(ci);
    ci := self.ClickedClip;
    FrmPermanent.ShowWithNewComplexItem(ci);
end;

procedure TFrmRemoved.CreateParams(var Params: TCreateParams);
begin
    inherited CreateParams(Params);
    // allow context taskbar context menu and
    // show me on the taskbar - independant of main form
    with Params do begin
        ExStyle := ExStyle or WS_EX_APPWINDOW;
        WndParent := GetDesktopwindow;
    end;
end;

procedure TFrmRemoved.CreatePlainTextClick(Sender: TObject);
begin
    self.FreeRemoved(ci);
    ci := self.ClickedClip;
    FrmPermanent.ShowWithNewItem(ci.GetAsText);
end;

procedure TFrmRemoved.FindDialog1Find(Sender: TObject);
var i,j, a,b : integer;
    s : string;
    pc : PChar;
    op : TStringSearchOptions;
begin
    if lblist.Count = 0 then EXIT;

    // select the first item in the list by default
    // translate the index because RemovedCach is stored oldest first
    if lbList.itemindex = -1 then begin
        lblist.ItemIndex := 0;
        lbListClick(lblist);
    end;
    i := lbList.ItemIndex;
    i := (lbList.Count-1) - i;

    op := [soDown];
    if frMatchCase in FindDialog1.options then op := op + [soMatchCase];
    if frwholeword in FindDialog1.options then op := op + [soWholeWord];

    // in an oldest-first list, down and up searches are reversed
    if (frdown in FindDialog1.Options) then begin
        a := 0;
        b := i;
        for j := b-1 downto a do begin
            s := RemovedQueue.GetItemText(j);
            pc := SearchBuf(
                pchar(s), length(s),
                0,0, FindDialog1.FindText,
                op
            );
            if pc <> nil then begin
                lblist.ItemIndex := (lblist.count-1)-j;
                lblist.TopIndex := (lblist.count-1)-j;
                lbListClick(lblist);
                EXIT;
            end;
        end;
    end else begin
        a := i;
        b := lbList.Count - 1;

        for j := a+1 to b do begin
            s := RemovedQueue.GetItemText(j);
            pc := SearchBuf(
                pchar(s), length(s),
                0,0, FindDialog1.FindText,
                op
            );
            if pc <>nil then begin
                lblist.ItemIndex := (lblist.count-1)-j;
                lblist.TopIndex := (lblist.count-1)-j;
                lbListClick(lblist);
                EXIT;
            end;
        end;
    end;
end;


procedure TFrmRemoved.FindDialog1Show(Sender: TObject);
begin
    SetForegroundWindow(finddialog1.Handle)
end;

procedure TFrmRemoved.btnEditClick(Sender: TObject);
var s : string;
    i : integer;
begin

    self.FreeRemoved(ci);
    i := (lbList.Count - 1) - lbList.ItemIndex;
    ci := RemovedQueue.GetItemClip(i);

    //self.Hide;
    frmEditTextExternal.EditClip(ci);
    self.Show;
    Application.ProcessMessages;

    self.FormShow(self);

    {
    if ci = nil then EXIT;
    if lblist.ItemIndex < 0 then EXIT;

    s := ci.GetAsText;
    if s = '' then begin
        s := RemovedQueue.GetItemText((lblist.count-1)-lbList.ItemIndex);
    end;
    FrmEditItem.SetText(s, self);

    //FrmEditItem.Position := poOwnerFormCenter;
    i := FrmEditItem.ShowModal;
    //FrmEditItem.Position := poDesigned;

    if i <> mrCancel then begin
        self.FormShow(self);
    end;
    }
end;

end.


