unit UnitFrmPasteSelected;
{
    Purpose:
        Display the clipboard
        User select 0 or more items
        All items are returned as a string - empty if none selected

    Updates:

        Fixed cancel button
        ------------

        Added option to reverse pasting order
        Using UnitClipQueue
}
interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, UnitTWideChar, Buttons, Menus;

type
  TFrmPasteSelected = class(TForm)
    lbHistory: TListBox;
    btnPaste: TButton;
    btnCancel: TButton;
    cbPasteReverse: TCheckBox;
    Label1: TLabel;
    SpeedButton1: TSpeedButton;
    SpeedButton2: TSpeedButton;
    pmPaste: TPopupMenu;
    SpeedButton3: TSpeedButton;
    Paste1: TMenuItem;
    PasteinReverse1: TMenuItem;
    ClipboardOnly1: TMenuItem;
    ClipboardOnlyinReverse1: TMenuItem;
    procedure FormShow(Sender: TObject);

    procedure btnCancelClick(Sender: TObject);
    procedure FormKeyUp(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure FormActivate(Sender: TObject);
    procedure lbHistoryMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure lbHistoryDrawItem(Control: TWinControl; Index: Integer;
      Rect: TRect; State: TOwnerDrawState);
    procedure lbHistoryMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure lbHistoryMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure SpeedButton1Click(Sender: TObject);
    procedure SpeedButton2Click(Sender: TObject);
    procedure SpeedButton3Click(Sender: TObject);
    procedure Paste1Click(Sender: TObject);
    procedure PasteinReverse1Click(Sender: TObject);
    procedure ClipboardOnly1Click(Sender: TObject);
    procedure ClipboardOnlyinReverse1Click(Sender: TObject);
    procedure lbHistoryMeasureItem(Control: TWinControl; Index: Integer;
      var Height: Integer);
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure FormKeyPress(Sender: TObject; var Key: Char);
  private
    { Private declarations }
    Cancelled : boolean;
    ClipboardOnly : boolean;
    mysel : array of boolean;
    shifted : boolean;
    shiftedIdx : integer;
    startingidx : integer;
    lastIdx : integer;
    mousereleased : boolean;
    procedure UpdateSelections;
    procedure StoreSelections;
  public
    { Public declarations }
    function GetSelectedItems() : string; overload;
//    function GetSelectedItemsWide() : TWideChar; overload;

    procedure ShowaAutomated;
  end;

var
  FrmPasteSelected: TFrmPasteSelected;

implementation

uses UnitFrmClipboardManager, UnitFrmMainPopup, UnitClipQueue , UnitMisc,
  UnitPaste, UnitPopupGenerate, Math, UnitFocusManager;

{$R *.dfm}

//----------------------------------
// public interface
//----------------------------------
function TFrmPasteSelected.GetSelectedItems() : string;
var i : longint;
    s : string;
    first, morethanone : boolean;
begin
    //
    // We need to steal focus to use this windows after the popup
    // ;otherwise, it will pop-under on the second call

    Windows.SetForegroundWindow(self.handle);
    self.ShowModal;
    first := true;
    morethanone := false;
    s := '';
    if (not self.Cancelled) then begin
        if cbPasteReverse.Checked then begin
            for i := (lbHistory.items.count - 1) downto 0 do begin
                if mysel[i] then begin
                    if (first) then begin
                        first := false;
                    end else begin
                        morethanone := true;
                    end;
                    if s = '' then begin
                        s := s + lbHistory.items[i];
                    end else begin
                        s := s + #13#10 + lbHistory.items[i];
                    end;
                end;
            end;
        end else begin
            for i := 0 to (lbHistory.items.count - 1) do begin
                if mysel[i] then begin
                    if (first) then begin
                        first := false;
                    end else begin
                        morethanone := true;
                    end;
                    if s = '' then begin
                        s := s + lbHistory.items[i];
                    end else begin
                        s := s + #13#10 + lbHistory.items[i];
                    end;
                end;
            end;
        end;
    end;
    if ClipboardOnly  then begin
        if morethanone then
            frmClipboardManager.DisablePasteProtectionOnce;
        paste.SendPlainText(s);
        s := '';
    end;

    result := s;
end;


//----------------------------------
// private implemenation
//----------------------------------

procedure TFrmPasteSelected.FormShow(Sender: TObject);

begin
    // get the latest list
    lbHistory.Items.Clear;
    ClipQueue.GetQueueItems(lbHistory.items);


    mousereleased := true;
end;



procedure TFrmPasteSelected.Paste1Click(Sender: TObject);
begin
    cbPasteReverse.Checked := false;
    self.Cancelled := false;
    self.close;
end;

procedure TFrmPasteSelected.PasteinReverse1Click(Sender: TObject);
begin
    cbPasteReverse.Checked := true;
    self.Cancelled := false;
    self.close;
end;

procedure TFrmPasteSelected.ClipboardOnly1Click(Sender: TObject);
begin
    self.Cancelled := false;
    self.ClipboardOnly := true;
    cbPasteReverse.Checked := false;
    self.close;
end;

procedure TFrmPasteSelected.ClipboardOnlyinReverse1Click(Sender: TObject);
begin
    self.Cancelled := false;
    self.ClipboardOnly := true;
    cbPasteReverse.Checked := true;
    self.Close;
end;

procedure TFrmPasteSelected.btnCancelClick(Sender: TObject);
begin
    self.Cancelled := true;
    self.close;
end;




procedure TFrmPasteSelected.FormKeyDown(Sender: TObject; var Key: Word;Shift: TShiftState);
var i,x : integer;
	r : TRect;
begin
    if self.ActiveControl = lbHistory then begin
        case key of
        vk_space: begin
            key := 0;
            //showmessage(inttostr(lbhistory.itemindex));
        	self.lbHistoryMouseDown(self, mbLeft,[],0,0);
            self.lbHistoryMouseUp(self, mbleft,[],0,0);
        end;
        end;

        if key in [VK_UP, VK_DOWN] then begin
        	x := lbhistory.Itemindex;
            lbHistory.Items.BeginUpdate;
            for i := 0 to lbhistory.Count - 1 do begin
            	if lbhistory.Selected[i] <> mysel[i]  then
            		lbhistory.Selected[i] := mysel[i];
            end;
            lbHistory.ItemIndex := x;
            lbHistory.Invalidate;

            if key = VK_UP then lbHistory.ItemIndex := max(0, lbHistory.ItemIndex-1);
            if key = VK_DOWN then lbHistory.ItemIndex := lbHistory.ItemIndex + 1 mod lbHistory.Count;

            lbHistoryDrawItem(lbhistory, lbHistory.ItemIndex,r,[odSelected]);

            lbhistory.Items.EndUpdate;
            key := 0;
        end;


    end;
end;

procedure TFrmPasteSelected.FormKeyPress(Sender: TObject; var Key: Char);
begin
    key := #0;
end;

procedure TFrmPasteSelected.FormKeyUp(Sender: TObject; var Key: Word;Shift: TShiftState);

begin
    if (KEY = VK_ESCAPE) then begin
        self.btnCancel.Click;
    end;

    if self.ActiveControl = lbhistory then begin


       { if key in [VK_UP, VK_DOWN] then begin
        	x := lbhistory.Itemindex;
            for i := 0 to lbhistory.Count - 1 do begin
            	lbhistory.Selected[i] := mysel[i];
            end;
            lbHistory.ItemIndex := x;

            key := 0;
        end;}
    end;
end;

procedure TFrmPasteSelected.FormActivate(Sender: TObject);
var i : integer;
begin
    self.lbHistory.SetFocus;
    setlength(mysel, lbHistory.Count);
    for i := low(mysel) to high(mysel) do begin
        mysel[i] := false;
    end;
end;

procedure TFrmPasteSelected.lbHistoryDrawItem(Control: TWinControl;
  Index: Integer; Rect: TRect; State: TOwnerDrawState);
var wc : TWideChar;
    ci : TClipItem;
    c : TColor;
const LEFT_SPACE = 8;
    TOP_SPACE = 2;
begin

    c := tlistbox(control).Canvas.Pen.Color;
    if tlistbox(control).Selected[index] or   (index = tlistbox(control).itemindex) then begin
        tlistbox(control).Canvas.Pen.Color := clHighlight;
        tlistbox(control).canvas.fillrect(rect);
    end else begin
        tlistbox(control).canvas.FillRect(rect);
    end;
    tlistbox(control).Canvas.Pen.Color := c;
    
    if UnitMisc.UniSupported then begin

        ci := ClipQueue.GetClipItem(index);
        wc := TWideChar.Create;
        wc.AppendUnicode(ci.GetAsPlaintext);

        Windows.TextOutW(
            TListBox(Control).Canvas.Handle,
            rect.Left + LEFT_SPACE, rect.Top + TOP_SPACE, wc.Memory, wc.StrLength
            );
    end else begin
        Windows.TextOut(
            TListBox(Control).Canvas.Handle,
            rect.Left + LEFT_SPACE, rect.Top + TOP_SPACE, pchar(lbHistory.items[index]) , length(lbHistory.Items[index])
            );
    end;
end;

//
// Toggle current only if one item is clicked
// highlight all dragged
//
procedure TFrmPasteSelected.lbHistoryMeasureItem(Control: TWinControl;
  Index: Integer; var Height: Integer);
begin
    Height := lbHistory.Canvas.TextHeight('AZ') + 4; 
end;

procedure TFrmPasteSelected.lbHistoryMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var i : integer;
begin
    lbHistory.Items.BeginUpdate;

        shifted := ssShift in shift;
        lastidx := lbhistory.ItemIndex;
        startingidx := lastidx;
        //mysel[lbHistory.ItemIndex] := not mysel[lbHistory.ItemIndex];
        for i := 0 to lbhistory.Count - 1 do begin
            lbhistory.Selected[i] := mysel[i];
        end;
        lbHistory.ItemIndex := lastidx;
        mousereleased := false;
    lbHistory.Items.EndUpdate;
end;

procedure TFrmPasteSelected.lbHistoryMouseMove(Sender: TObject;
  Shift: TShiftState; X, Y: Integer);
var i : integer;
begin
    // highlight all using drag select
    if not mousereleased then begin
        i := lbHistory.ItemAtPos(point(x,y),true);
        if (i <> -1) and (i<>lastidx) then begin
            if not mysel[startingidx] then begin
                mysel[startingidx] := true;
                lbHistory.Selected[startingidx] := mysel[startingidx];
            end;

            mysel[i] := true;
            lbHistory.Selected[i] := mysel[i];
            lastidx := i;
            lbHistory.ItemIndex := i;
        end;
    end;
end;

procedure TFrmPasteSelected.lbHistoryMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

begin
    // detect single item clicked
    if lbHistory.ItemIndex = startingidx then begin
        mysel[lastidx] := not mysel[lastidx];
        lbHistory.Selected[lastidx] := mysel[lastidx];
    end;
    lbHistory.ItemIndex := lastidx;
    mousereleased := true;
end;



procedure TFrmPasteSelected.ShowaAutomated;
var s : string;
begin
    s := self.GetSelectedItems;
    if s <> '' then begin
        TFocusManager.ForceForeground(FrmMainPopup.targetdata.ForegroundWindow);
        Windows.SetForegroundWindow(FrmMainPopup.TargetData.ForegroundWindow);
        if (FrmMainPopup.TargetData.focuscontrol <> 0) then begin
            Windows.SetFocus(FrmMainPopup.TargetData.focuscontrol);
        end;

        Paste.SendPlainText(s);
    end;
end;

procedure TFrmPasteSelected.SpeedButton1Click(Sender: TObject);
var i : integer;
begin
    for i := low(mysel) to high(mysel) do begin
        mysel[i] := true;
        lbHistory.selected[i] := mysel[i];
    end;
end;

procedure TFrmPasteSelected.SpeedButton2Click(Sender: TObject);
var i : integer;
begin
    for i := low(mysel) to high(mysel) do begin
        mysel[i] := false;
        lbHistory.selected[i] := mysel[i];
    end;
end;

procedure TFrmPasteSelected.SpeedButton3Click(Sender: TObject);
begin
    //self.Cancelled := false;
    ShowPopupRight(speedbutton3, pmPaste);
end;

procedure TFrmPasteSelected.StoreSelections;
var i : integer;
begin
    for i := 0 to lbhistory.Count - 1 do begin
        if lbhistory.Selected[i] then begin
            mysel[i] := true;
        end;
    end;
end;

procedure TFrmPasteSelected.UpdateSelections;
var i, idx : integer;
begin
    with lbhistory do begin
        idx := ItemIndex;
        if shifted then begin
            for i := 0 to (Count - 1) do begin
                if (lastidx < shiftedIdx) then begin
                    mysel[i] := (i >= lastidx) and (i <= shiftedIdx);
                end else begin
                    mysel[i] := (i >= shiftedIdx) and (i <= lastidx);
                end;
            end;
        end else begin
            mysel[ItemIndex] := not mysel[ItemIndex];
        end;

         for i := 0 to lbhistory.Count - 1 do begin
            lbhistory.Selected[i] := mysel[i];
         end;
         ItemIndex := idx;
    end;
end;

end.
