unit UnitFramePermanentClips;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, UnitFrameClipDisplay,
  Vcl.Buttons, Vcl.ExtCtrls, Vcl.StdCtrls, Vcl.Menus;

type
  TFramePermanentClips = class(TFrame)
    cpgGroups: TCategoryPanelGroup;
    cpNewGroup: TCategoryPanel;
    Label1: TLabel;
    txtPermGroup: TEdit;
    btnPermCreate: TButton;
    pnlHotkey: TPanel;
    btnSetHotkey: TSpeedButton;
    btnClearHotkey: TSpeedButton;
    pnl1: TPanel;
    Label2: TLabel;
    lblHotkey: TLabel;
    FramePermClip: TFrameClipDisplay;
    pmPermMenu: TPopupMenu;
    MIEdit1: TMenuItem;
    N2: TMenuItem;
    MIMoveTo1: TMenuItem;
    MIDelete1: TMenuItem;
    pnl3: TPanel;

    lblMacroHelp: TLabel;
    procedure btnPermCreateClick(Sender: TObject);
    procedure btnSetHotkeyClick(Sender: TObject);
    procedure btnClearHotkeyClick(Sender: TObject);

  private
    { Private declarations }

     slGroups : TStringList;
        lastGroup, lastHotkey : string;
        lastClip : integer;

    procedure pmPermMenuPopup(Sender: TObject);
    procedure txtPermGroupChange(Sender: TObject);
    procedure MIEdit1Click(Sender: TObject);
    procedure MINewClip1Click(Sender: TObject);

            procedure resetView;
        procedure showPermClip(index : integer);
        procedure btnPermEdit(Sender : TObject);
        procedure btnPermNew(Sender : TObject);

        procedure btnPermGroupDelete(Sender : TObject);

        procedure groupExpand(Sender : TObject);
        procedure permanentMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
        procedure permMenuItemClick(Sender : TObject);
        procedure permanentDoubleClick(Sender : TObject);

        procedure permanentDragDrop(Sender : TObject; Source:TObject; x : integer; y : integer);
        procedure permanentDragOver(Sender : TObject; Source:TObject; x : integer; y : integer; state : TDragState; var accept : boolean);

        procedure btnPermDelete(Sender : TObject);
     procedure cpgGroupsMouseWheel(Sender: TObject;
      Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint;
      var Handled: Boolean);
  public
    { Public declarations }
    procedure ShowPermGroup(group : string; clipIndex : integer=-1);
    procedure loadPermanent;
    procedure positionPermanentEdit;
  end;

implementation

{$R *.dfm}

uses UnitClipDatabase, UnitFrmPermanentEdit, UnitMisc, UnitFrmHotKey,
  UnitFrmPermanentNew, UnitClipQueue, Vcl.ComCtrls, Math;



//
// Permanent Clips/Macros
//

type TListUsed = class(TListBox);
type TCategoryPanelGroupHelper = class helper for TCustomCategoryPanelGroup
  public
    procedure RemovePanel_(Panel: TCustomCategoryPanel);
  end;
procedure TCategoryPanelGroupHelper.RemovePanel_(Panel: TCustomCategoryPanel);
begin
  Self.RemovePanel(Panel);
end;
type TCategoryPanelHelper = class helper for TControl
    public
        procedure setHeight_(height : integer);
end;
procedure TCategoryPanelHelper.setHeight_(height : integer);
begin
    self.fheight := height;
end;

const NEW_GROUP_LABEL = '[New Group]';

procedure TFramePermanentClips.showPermClip(index : integer);
var
    ci : TClipItem;
    lastHotkey : string;
begin
    ci := TClipItem.Create;
    TClipDatabase.LoadPermanent(ci, index, lastGroup);
    lastHotkey := TClipDatabase.LoadPermanentHotkey(index, lastGroup);
    lblHotkey.Caption := unitmisc.hotkeyName(lastHotkey);

    FramePermClip.ShowClip(ci);
    myfree(ci);
end;

procedure TFramePermanentClips.positionPermanentEdit;
var
    pt : TPoint;
begin
    pt.X := self.Left;
    pt.Y := self.Top;

    pt := self.ClientToScreen(pt);
    FrmPermanentEdit.Top := pt.Y;
    FrmPermanentEdit.Left := pt.X;
end;
procedure TFramePermanentClips.loadPermanent;
var
    i : integer;
    s, key : string;
    mi : TMenuItem;
    procedure showGroup(group : string);
    var
        sl, keys : TStringList;
        s : string;
        pnl : TCategoryPanel;
        lb : TListUsed;
        li : TListItem;
        pnl2 : TPanel;
        btn, btn2 : TButton;
    begin
        pnl := TCategoryPanel.Create(cpgGroups);
        pnl.PanelGroup := cpgGroups;
        pnl.Caption := group;
        pnl.Hint := group;
        pnl.OnExpand := self.groupExpand;
        pnl.Padding.Top := 5;


        lb := TListUsed.Create(pnl);
        lb.Parent := pnl;
        lb.OnMouseUp := self.permanentMouseUp;
        lb.OnDblClick := self.permanentDoubleClick;
        lb.OnDragDrop := self.permanentDragDrop;
        lb.OnDragOver := self.permanentDragOver;
        lb.DragMode := dmAutomatic;
        lb.ParentFont := false;
        lb.Font := cpgGroups.Font;
        lb.font.size := 9;
        lb.BorderStyle := bsNone;

        sl := TStringList.Create;
        TClipDatabase.LoadPermanentNames(sl, group);
        keys := TStringList.Create;
        TClipDatabase.LoadPermanentHotkeys(keys, group);
        for s in sl do begin
            key := '';
            if keys.Count > lb.Items.count then
                key := keys[lb.Items.Count];
            if (length(key) > 6) then begin
                delete(key,1,6);
                lb.Items.Add(s + '  ['+key+']');
            end else begin
                lb.Items.Add(s);
            end;
        end;
        i := trunc(lb.Canvas.TextHeight('ASDF') * 1.15);
        lb.height := max( i * (lb.Items.Count) + 8, 30 );


        pnl2 := TPanel.Create(pnl);
        pnl2.Parent := pnl;
        pnl2.BevelOuter := bvNone;
        btn := TButton.Create(pnl2);
        btn.Parent := pnl2;
        btn.Caption := 'New';
        btn.Align := alRight;
        btn.OnClick := self.btnPermNew;

        btn2 := TButton.Create(pnl2);
        btn2.Parent := pnl2;
        btn2.Caption := 'Delete Group';
        btn2.Align := alLeft;
        btn2.OnClick := self.btnPermGroupDelete;
        btn2.Width := 90;
        btn2.Visible := sl.Count = 0;
        btn2.Padding.Left := 10;

        pnl2.Height := 37;
        pnl2.Padding.Right := 20;
        pnl2.Padding.Top := 10;
        pnl2.Padding.Bottom := 3;

        pnl2.Align := alTop;
        lb.Align := alTop;

        pnl.setHeight_(lb.height + (i*3) + pnl2.height );
        pnl.Collapsed := true;
    end;

var
    pnl : TCategoryPanel;
begin
    lastClip := -1;
    lastGroup := '';

    lblMacroHelp.Caption := '';

    if (slGroups = nil) then begin
        slGroups := TStringList.Create;
    end;
    cpgGroups.OnMouseWheel := self.cpgGroupsMouseWheel;
    MIDelete1.OnClick := btnPermDelete;
    MIEdit1.OnClick := btnPermEdit;
    btnPermCreate.OnClick := btnPermCreateClick;
    btnSetHotkey.OnClick := btnSetHotkeyClick;
    btnClearHotkey.OnClick := btnClearHotkeyClick;
    pmPermMenu.OnPopup := pmPermMenuPopup;

    cpNewGroup.OnExpand := self.groupExpand;
    cpNewGroup.Collapsed := true;

    cpgGroups.Invalidate;
    for i:= cpgGroups.Panels.Count-1 downto 0 do begin
        pnl := cpgGroups.Panels[i];
        if (pnl.Caption <> NEW_GROUP_LABEL) then
            cpgGroups.RemovePanel_(pnl);
        cpgGroups.Invalidate;
    end;

    slGroups.Clear;


    TClipDatabase.StartBatch;
    TClipDatabase.LoadPermanentGroups(slGroups);

    for s in slGroups do begin
        showGroup(s);
        cpgGroups.Invalidate;
    end;

    TClipDatabase.EndBatch;


end;
procedure TFramePermanentClips.ShowPermGroup(group : string; clipIndex : integer=-1);
var
    i : integer;
    cp : TCategoryPanel;
    lb : TListUsed;
begin
    resetView;
    for i := 0 to cpgGroups.Panels.Count-1 do begin
        cp := cpgGroups.Panels[i];
        if (cp.hint = group) then begin
            lastGroup := group;
            cp.Collapsed := false;

            if (clipIndex <> -1) then begin
                lb := TListUsed( TWinControl(cp.Controls[0]).Controls[0]);
                lb.ItemIndex := clipIndex;
                self.showPermClip(clipIndex);
                lastClip := clipIndex;
            end;
            EXIT;
        end;
    end;
end;
procedure TFramePermanentClips.resetView;
begin
    lblHotkey.Caption := '';
    lastClip := -1;
    FramePermClip.ShowClip('');
    FramePermClip.PicStretched := true;


    lblMacroHelp.Caption := '';
end;


procedure TFramePermanentClips.btnSetHotkeyClick(Sender: TObject);
var
    hkd : THotkeyData;
    s, g : string;
    i : integer;
begin
    if lastClip = -1 then EXIT;

    if FrmHotkey.GetHotkey(hkd) then begin
        s := FrmHotkey.ToHKString(hkd);
        i := lastClip;
        g := lastGroup;
        TClipDatabase.SavePermanentHotkey(s,lastClip,lastGroup );
        lblHotkey.Caption := unitmisc.hotkeyName(s);
        FrmPermanent.RefreshHotkeys;
        self.loadPermanent;
        self.ShowPermGroup(g,i);
    end;
end;
procedure TFramePermanentClips.btnClearHotkeyClick(Sender: TObject);
var
    g : string;
    i : integer;
begin
    i := lastClip;
    g := lastGroup;
    TClipDatabase.SavePermanentHotkey('',lastClip,lastGroup);
    lblHotkey.Caption := '';
    FrmPermanent.RefreshHotkeys;
    self.loadPermanent;
    self.ShowPermGroup(g,i);
end;

procedure TFramePermanentClips.permanentMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
    lb : TListUsed;
    cp : TCategoryPanel;
    cpg : TCategoryPanelGroup;
    i : integer;
    pt : TPoint;
    ci : TClipITem;
    li : TListItem;
    procedure setButtons;
    begin

    end;
begin
    if (sender is TListUsed) then begin

        lb := TListUsed(Sender);
        resetView;
        FramePermClip.PicStretched := true;
        case Button of
        mbLeft: begin
            lastClip := lb.ItemIndex;
            setButtons;
            if lastClip = -1 then EXIT;

            self.showPermClip(lastClip);
        end;
        mbRight: begin
            pt := lb.ScreenToClient(mouse.CursorPos);
            lastClip := lb.ItemAtPos(pt, true);
            lb.ItemIndex := lastClip;
            setButtons;
            if lastClip=-1 then EXIT;

            self.showPermClip(lastClip);
            pmPermMenu.Popup(mouse.CursorPos.X + 10, mouse.CursorPos.y + 5);
        end;
        end;
    end;
end;
procedure TFramePermanentClips.permanentDoubleClick(Sender : TObject);
var
    lb : TListUsed;
begin
    if (sender is TListUsed) then begin
        self.permanentMouseUp(Sender, mbRight,[],-1,-1);
    end;
end;
procedure TFramePermanentClips.permanentDragDrop(Sender : TObject; Source:TObject; x : integer; y : integer);
var
    i, j : integer;
    lb : TListUsed;
    s : string;
begin
    lb := TlistUsed(Sender);
    i := lb.ItemAtPos(point(x,y),true);
    j := lb.ItemIndex;

    FrmPermanent.PermFolderPush;
    FrmPermanent.SetPermanentPath(lastGroup);
    FrmPermanent.MoveClip(j, i);
    FrmPermanent.PermFolderPop;


    s := lastGroup;
    self.loadPermanent;
    self.ShowPermGroup(s);
    FrmPermanent.RefreshHotkeys;
end;
procedure TFramePermanentClips.permanentDragOver(Sender : TObject; Source:TObject; x : integer; y : integer; state : TDragState; var accept : boolean);
var
    i : integer;
    lb : TListUsed;
begin
    lb := TlistUsed(Sender);
    i := lb.ItemAtPos(point(x,y),true);

    accept := i <> -1;
    accept := accept and (i <> lb.ItemIndex);
end;



procedure TFramePermanentClips.btnPermGroupDelete(Sender : TObject);
begin
    if MessageDlg('Delete this entire ['+lastGroup+'] group?', mtConfirmation,[mbYes, mbNo],0,mbNo) <> mrYes  then
        EXIT;

    TClipDatabase.ClearPermanentGroup(lastGroup, true);
    self.loadPermanent;
end;
procedure TFramePermanentClips.btnPermEdit(Sender : TObject);
var s : string;
begin
    s := lastGroup;
    self.positionPermanentEdit;
    if FrmPermanentEdit.ShowForm(lastGroup, lastClip) = mrOK then begin
        self.loadPermanent;
        self.ShowPermGroup(s);
    end;
end;
procedure TFramePermanentClips.btnPermNew(Sender : TObject);
var s : string;
begin
    s := lastGroup;
    self.positionPermanentEdit;
    if FrmPermanentEdit.ShowFormNew(lastGroup) = mrOK then begin
        self.loadPermanent;
        self.ShowPermGroup(s);
    end;
end;
procedure TFramePermanentClips.groupExpand(Sender : TObject);
var i,j : integer;
    cp : TCategoryPanel;
    ci : TClipItem;
    lb : TListUsed;
    isNewGroup : boolean;
begin
    resetView;

    isNewGroup := false;
    for i := 0 to cpgGroups.Panels.Count-1 do begin
        cp := cpgGroups.Panels[i];
        if (sender <> cp) then begin
            if (not cp.Collapsed) then begin
                cp.Collapse;
            end;
        end else begin
            if (cp.Caption = NEW_GROUP_LABEL) then begin
                txtPermGroup.Text := '';
                txtPermGroup.SetFocus;
                isNewGroup := true;

                CONTINUE;
            end;

            lb := TListUsed(TWinControl(cp.Controls[0]).Controls[0]);
            lb.ItemIndex := -1;
            lastGroup := cp.hint;
            lastClip := -1;
            CONTINUE;

            ci := TClipItem.Create;
            TClipDatabase.LoadPermanent(ci, lastClip, lastGroup);
            FramePermClip.ShowClip(ci);
            myfree(ci);



        end;
    end;

    if (isNewGroup) then begin
        lblMacroHelp.Caption := 'Create a new group for storing macros.' +
        ' Groups are used to categorize similar type macros.';
    end else if (lastGroup = SYSTEM_FOLDER) then begin
        lblMacroHelp.Caption := 'Unlike other groups, _System is not shown on the popup.'
        +' The macros in this group are designed to be used by hotkeys.';
    end else if (lastGroup = FORM_MODE_FOLDER) then begin
        lblMacroHelp.Caption := 'Unlike other groups, _Form Mode is not shown on the popup.'
        +' These macros appear in the Pasting Tools menu on the Popup.';
    end else if (lastGroup = DEFAULT_FOLDER) then begin
        lblMacroHelp.Caption := 'The "Default" group is shown on the popup when used in a new program.'
        +' The popup will remember the last group used for each program.';
    end else begin

    end;
end;
procedure TFramePermanentClips.txtPermGroupChange(Sender: TObject);
begin
    btnPermCreate.Enabled := txtPermGroup.Text <> '';
end;
procedure TFramePermanentClips.pmPermMenuPopup(Sender: TObject);
var
    s : string;
    mi : TMenuItem;
begin
    MIMoveTo1.Clear;
    for s in slGroups do begin
        if (s <> lastGroup) then begin
            mi := TMenuItem.Create(MIMoveTo1);
            mi.Caption := s;
            mi.hint := s;
            mi.OnClick := self.permMenuItemClick;
            MIMoveTo1.Add(mi);
        end;
    end;
end;
procedure TFramePermanentClips.btnPermCreateClick(Sender: TObject);
var
    group : string;
begin
    group := txtPermGroup.text;
    if slGroups.IndexOf(group) = -1 then begin
        TClipDatabase.SavePermanentGroup(group, 0);
        self.loadPermanent;
        self.ShowPermGroup(group);

        FrmPermanent.RefreshGroups;
    end;
end;
procedure TFramePermanentClips.permMenuItemClick(Sender : TObject);
var
    group : string;
begin
    group := TMenuItem(Sender).hint;

    TClipDatabase.MovePermanentClip(lastClip, lastGroup, group);
    self.loadPermanent;
    self.ShowPermGroup(group);
end;
procedure TFramePermanentClips.btnPermDelete(Sender : TObject);
var s : string;
begin
    if MessageDlg('Delete the selected item ?', mtConfirmation,[mbYes, mbNo],0,mbNo) <> mrYes  then
        EXIT;
    s := lastGroup;
    TClipDatabase.DeletePermanentClip(lastClip, lastGroup);
    self.loadPermanent;
    self.ShowPermGroup(s);

    FrmPermanent.RefreshGroups;
    FrmPermanent.RefreshHotkeys;
end;
procedure TFramePermanentClips.MIEdit1Click(Sender: TObject);
begin
    btnPermEdit(Sender);
end;
procedure TFramePermanentClips.MINewClip1Click(Sender: TObject);
begin
    btnPermNew(Sender);
end;

procedure TFramePermanentClips.cpgGroupsMouseWheel(Sender: TObject;
  Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint;
  var Handled: Boolean);
var
    direction : integer;
begin
    Handled := true;
    direction := -3;
    if wheelDelta < 0 then direction := 3;
    cpgGroups.VertScrollBar.Position :=
        cpgGroups.VertScrollBar.ScrollPos + cpgGroups.VertScrollBar.Increment * direction;
end;

end.
