unit unit1;
//Yua Ar1ga
{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, ShellCtrls, SynEdit,
  SynHighlighterAny, ComCtrls, Menus, StdCtrls, Grids, StrUtils, FileUtil, LazFileUtils,
  SynEditTypes, LCLType, ECRuler, TerminalCtrls, Types, Process, SynEditKeyCmds,
  LCLIntf;

type

  { TForm1 }

  TForm1 = class(TForm)
				CursorPos:TLabel;

    //  TOP LEVEL MENU
    MainMenu1: TMainMenu;

    PopupFavourites1: TMenuItem;
    PopupFavourites2: TMenuItem;
    ProjectMenu: TMenuItem;
    SCD: TButton;
    SCB: TButton;
    SelectRoot: TMenuItem;
    FileMenu: TMenuItem;
    M_Save: TMenuItem;
    M_SaveAs: TMenuItem;
    M_Quit: TMenuItem;
		Separator1: TMenuItem;
		M_CompareFiles: TMenuItem;

    ToolsMenu: TMenuItem;
    M_Search: TMenuItem;

    EditMenu: TMenuItem;
    M_Find: TMenuItem;
    M_FindNext: TMenuItem;
    M_FindPrevious: TMenuItem;
    M_Bookmark: TMenuItem;

    M_Help: TMenuItem;
    M_New: TMenuItem;
    M_Navigate: TMenuItem;
    M_Execute: TMenuItem;
    M_Output: TMenuItem;
    Helpmenu: TMenuItem;

    PanelA: TPanel;
    Left_PageControl: TPageControl;
    Left_TabSheet1: TTabSheet;
    ShellTreeView1: TShellTreeView;
    PopupMenu1: TPopupMenu;
    PopupEdit1: TMenuItem;
    PopupCopy1: TMenuItem;
    PopupMove1: TMenuItem;
    PopupMkDir1: TMenuItem;
    PopupDelete1: TMenuItem;
    PopupFavourites: TMenuItem;
    ShellTreeView2: TShellTreeView;
    PopupMenu2: TPopupMenu;
    PopupEdit2: TMenuItem;
    PopupCopy2: TMenuItem;
    PopupMove2: TMenuItem;
    PopupMkDir2: TMenuItem;
    PopupDelete2: TMenuItem;
    Left_TabSheet2: TTabSheet;
    CodeView: TTreeView;

    PanelA3: TPanel;
    B_Edit: TButton;
    B_Copy: TButton;
    B_Move: TButton;
    B_MkDir: TButton;
    B_Delete: TButton;

    PanelB: TPanel;
    Edit_PageControl: TPageControl;

    Splitter1: TSplitter;

    Terminal1: TTerminal;

    PanelC: TPanel;
    PanelC2: TPanel;
    SC0: TButton;
    SC1: TButton;
    SC2: TButton;
    SC3: TButton;
    SC4: TButton;
    SC5: TButton;
    SC6: TButton;
    SC7: TButton;
    TerminalOut: TMemo;
    TerminalIn: TEdit;

    PanelD: TPanel;
    Tools_PageControl: TPageControl;
    Tools_Find: TTabSheet;
    Label1: TLabel;
    T_Search: TEdit;
    Label2: TLabel;
    T_Replace: TEdit;
    CheckBox1_CaseSensitive: TCheckBox;
    CheckBox2_WholeWord: TCheckBox;
    CheckBox3_PromptOnReplace: TCheckBox;
    RadioButton1_Forward: TRadioButton;
    RadioButton2_Backward: TRadioButton;
    B_Find: TButton;
    B_Replace: TButton;
    B_ReplaceAll: TButton;

    Tools_Navigate: TTabSheet;
    Label3: TLabel;
    B_Jump: TButton;
    T_Jump_LineNo: TEdit;
    BookmarkGrid: TStringGrid;

    Tools_Execute: TTabSheet;
    RunExtGrid: TStringGrid;

    Tools_Output: TTabSheet;
    Output_Memo: TMemo;

    Tools_Help: TTabSheet;
    HelpGrid1: TStringGrid;


    //  FORM
    procedure FormCreate(Sender: TObject);
    procedure FormKeyUp(Sender: TObject; var Key: word; Shift: TShiftState);
    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    procedure Left_PageControlChange(Sender: TObject);

    //  TOPLEVEL MENUs
    procedure ProjectMenuClick(Sender: TObject);
    procedure FileMenuClick(Sender: TObject);
    procedure ToolsMenuClick(Sender: TObject);
    procedure EditMenuClick(Sender: TObject);
    procedure HelpMenuClick(Sender: TObject);

    //  BROWSERS
    procedure PopupMenu1Popup(Sender: TObject);
    procedure PopupMenu2Popup(Sender: TObject);
    procedure BrowserButtonsClick(Sender: TObject);
    procedure PopupFavouritesClick(Sender: TObject);

    procedure ShellTreeView1Click(Sender: TObject);
    procedure ShellTreeView1DblClick(Sender: TObject);
    procedure ShellTreeView1Expanded(Sender: TObject; Node: TTreeNode);
    procedure ShellTreeView1SelectionChanged(Sender: TObject);

    procedure ShellTreeView2Click(Sender: TObject);
    procedure ShellTreeView2DblClick(Sender: TObject);
    procedure ShellTreeView2Expanded(Sender: TObject; Node: TTreeNode);
    procedure ShellTreeView2SelectionChanged(Sender: TObject);

    //  CODE VIEW
    procedure CodeViewDblClick(Sender: TObject);

    //  EDITOR
    procedure Edit_PageControlChange(Sender: TObject);
    procedure Edit_PageControlCloseTabClicked(Sender: TObject);

    //  TOOLS
    procedure FindReplaceClick(Sender: TObject);
    procedure B_JumpClick(Sender: TObject);
    procedure BookmarkGridDblClick(Sender: TObject);
    procedure RunExtGridDblClick(Sender: TObject);
    procedure RunExtGridCheckboxToggled(Sender: TObject; aCol, aRow: integer;
      aState: TCheckboxState);
    procedure ShortcutClick(Sender: TObject);

    //  TERMINAL TerminalIn
    procedure TerminalInKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);

  private
  public

  end;

  TFiles = record
    Name: string;
    codeTree: array [0..15] of string;
    codeTreeIdx: int64;
  end;

var
  Form1: TForm1;

  treeView1RootNode: TTreeNode;
  treeView1CurrentNode: TTreeNode;
  treeView2RootNode: TTreeNode;
  treeView2CurrentNode: TTreeNode;
  ParentNode: TTreeNode;
  saveNode: TTreeNode;

  files: array [0..15] of TFiles;
  filesIdx: int64 = -1;
  initCodeTree: array [0..15] of string;
  currentTab: int64 = -1;
  deletedIdx: int64 = -1;

  inFile: TextFile;
  outFile: TextFile;

  J: int64;
  V: integer;
  W: integer;
  X: integer;
  Y: integer;
  Z: integer;

  Str1: string;
  Str2: string;
  Str3: string;
  Str4: string;
  Str5: string;
  UStr: string;

  message: string;

  activeTree: integer = 0;
  directoryFlag: boolean;

  fullPathSrc: string;
  dir1Empty: boolean;
  fullPathDst: string;
  dir2Empty: boolean;
  startupDir: string;

  FocusedSynEdit: TSynEdit;
	SynEdit0			: TSynEdit;
	SynEdit1			: TSynEdit;

  temp: TStringList;

  toolsYes: boolean;
  bookmarkRow: integer;
  FiredOnce: boolean;
  whitespace: boolean;
  tabWidth: int64;

  cmds: TStringList;
  cmdsIdx: int64 = -1;

  pwd					: string;

implementation

//FILE MENU
procedure SaveFile(); forward;
procedure SaveFileAs(); forward;
procedure QuitFile(); forward;
procedure shuffleFiles(); forward;
//VIEW MENU
procedure Tools(); forward;
//EditMenu MENU
procedure Find(Sender: TObject); forward;
//BROWSER BUTTONS
procedure x_Edit(); forward;
procedure x_Copy(); forward;
procedure fileCopy(); forward;
procedure x_Move(); forward;

procedure x_Delete(); forward;
procedure fileDelete(); forward;
procedure dirDelete(); forward;
procedure x_MkDir(Sender: TObject); forward;
procedure makeDir(); forward;

procedure CopyDirectory(sourceDir, destDir: string); forward;
procedure AddBookmark(Sender: TObject); forward;

procedure NewSynEdit(); forward;
procedure Highlight(ASynAnySyn: TSynAnySyn); forward;

procedure CodeTreeView(Str1: string); forward;
procedure runExternal(dir, prog, param: string); forward;

function IsDirectoryEmpty(Directory: string): boolean; forward;
procedure Terminal(Str1: string); forward;

function validOption(Key: word; Shift: TShiftState): boolean; forward;
procedure Shortcuts(); forward;
function GetParameter(s: string): string; forward;


{$R *.lfm}

{ TForm1 }

//=====================================================
//  Form Create
//=====================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  SubItem: TMenuItem;
  RunExt: array of string;
  Keystroke: TSynEditKeyStroke;
begin

  //---------------
  //  LOAD INI File
  //---------------
  GetDir(0, startupDir);
  Str1 := startupDir + '/editorONE.ini';
  assignFile(inFile, Str1);
  reset(inFile);

  readLn(inFile, Str1);
  Str1 := PChar(Str1.TrimLeft);
  UStr := UpperCase(Str1);
  while not EOF(inFile) do
  begin

    if UStr = '<FAVOURITES>' then
    begin
      readLn(inFile, Str1);
      Str1 := PChar(Str1.TrimLeft);
      while not EOF(inFile) do
      begin
        if LeftStr(Str1, 2) = '</' then break;

        SubItem := TMenuItem.Create(Self);
        SubItem.Caption := Str1;
        SubItem.OnClick := @ProjectMenuClick;
        ProjectMenu.Add(subItem);

        readLn(inFile, Str1);
        Str1 := PChar(Str1.TrimLeft);
      end; // END WHILE
    end // END BEGIN
    ; // END IF

    if UStr = '<TABWIDTH>' then
    begin
      readLn(inFile, Str1);
      Str1 := PChar(Str1.TrimLeft);
      while not EOF(inFile) do
      begin
        if LeftStr(Str1, 2) = '</' then break;

        tabWidth := StrToInt(Str1);

        readLn(inFile, Str1);
        Str1 := PChar(Str1.TrimLeft);
      end; // END WHILE
    end // END BEGIN
    ; // END IF

    if UStr = '<BUILD>' then
    begin

      for Y := 0 to 15 do
        Form1.RunExtGrid.Cells[2, Y] := '0';

      readLn(inFile, Str1);
      Str1 := PChar(Str1.TrimLeft);
      Y := 0;
      while not EOF(inFile) do
      begin
        if LeftStr(Trim(Str1), 2) = '</' then break;

        RunExt := Str1.split([' ']);
        Inc(Y);
        Form1.RunExtGrid.Cells[1, Y] := RunExt[0];      // Name
        Form1.RunExtGrid.Cells[2, Y] := RunExt[1];      // Shortcut
        Form1.RunExtGrid.Cells[3, Y] := RunExt[2];      // Work Directory
        Form1.RunExtGrid.Cells[4, Y] := RunExt[3];      // Program
        Form1.RunExtGrid.Cells[5, Y] := RunExt[4];      // Parameter

        readLn(inFile, Str1);
        Str1 := PChar(Str1.TrimLeft);

      end; // END WHILE
    end // END BEGIN
    ; // END IF

    readLn(inFile, Str1);
    Str1 := PChar(Str1.TrimLeft);
    UStr := UpperCase(Str1);

  end; // END WHILE
  closefile(inFile);

  //M_Help

  with HelpGrid1 do
  begin
    Cells[1, 1] := 'F1';
    Cells[2, 1] := 'Displays the Project/File Trees';
    Cells[1, 2] := 'F2';
    Cells[2, 2] := 'Displays the CodeTree';
    Cells[1, 3] := 'Ctrl+Shift+T';
    Cells[2, 3] := 'Reload CodeTree';

    Cells[1, 4] := 'Ctrl+N';
    Cells[2, 4] := 'New File';
    Cells[1, 5] := 'Ctrl+Shift+F3';
    Cells[2, 5] := 'Find Previous';
    Cells[1, 6] := 'Ctrl+Shift+S';
    Cells[2, 6] := 'Save As';
    Cells[1, 7] := 'Ctrl+Q';
    Cells[2, 7] := 'Quit';

    Cells[1, 8] := 'Alt+S';
    Cells[2, 8] := 'Search/Replace';
    Cells[1, 9] := 'Alt+N';
    Cells[2, 9] := 'Navigate';
    Cells[1, 10] := 'Alt+X';
    Cells[2, 10] := 'Build';
    Cells[1, 11] := 'Alt+O';
    Cells[2, 11] := 'Output';
    Cells[1, 12] := 'Alt+H';
    Cells[2, 12] := 'Help';

    Cells[1, 13] := 'Ctrl+F';
    Cells[2, 13] := 'Search/Replace';
    Cells[1, 14] := 'F3';
    Cells[2, 14] := 'Find Next';
    Cells[1, 15] := 'Shift+F3';
    Cells[2, 15] := 'Find Previous';
    Cells[1, 16] := 'Ctrl+B';
    Cells[2, 16] := 'Bookmark';
    Cells[1, 17] := 'Ctrl+W';
    Cells[2, 17] := 'Toggle Whitespace';

    Cells[1, 18] := 'Ctrl+U';
    Cells[2, 18] := 'UpperCase';
    Cells[1, 19] := 'Ctrl+L';
    Cells[2, 19] := 'LowerCase';

    Cells[1, 20] := 'Alt+Shift+S';
    Cells[2, 20] := 'Sort';

    Cells[1, 21] := 'Alt+T';
    Cells[2, 21] := 'Toggle Terminal';

    Cells[1, 22] := 'Ctrl+Alt+C';
    Cells[2, 22] := 'Compare Files';

  end // END BEGIN
  ; // END WITH

  //FORMWORK
  PanelD.Visible := False;
  PanelB.Height := 874;
  PanelC.Height := 874;
  toolsYes := False;

  ShellTreeView1.Root := '/home/roger/_dev1/source/languageONE/';
  treeView1RootNode := ShellTreeView1.Items.GetFirstNode;
  ShellTreeView1.FileSortType := fstFoldersFirst;
  treeView1CurrentNode := treeView1RootNode;
  ShelltreeView1.Select(treeView1RootNode);

  ShellTreeView2.Root := '/home/roger/_dev1/source/languageONE/';
  treeView2RootNode := ShellTreeView2.Items.GetFirstNode;
  ShellTreeView2.FileSortType := fstFoldersFirst;
  treeView2CurrentNode := treeView2RootNode;
  ShelltreeView2.Select(treeView2RootNode);

  Form1.Left_PageControl.ActivePage := Form1.Left_TabSheet1;
  Form1.Left_TabSheet1.Font.Color := clYellow;
  Form1.Left_TabSheet2.Font.Color := clDefault;

  ShellTreeView2Click(Sender);
  whitespace := False;

  //Form to get Keystroke 1st
  KeyPreview := True;

  cmds := TStringList.Create;

  Form1.TerminalOut.Visible := False;
  Form1.TerminalIn.Visible := False;
  Form1.Terminal1.Visible := True;

  Shortcuts();

end;

//-----------------------------------------------------
//  Form Close
//-----------------------------------------------------
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
var
  submenu: TMenuItem;
begin
  GetDir(0, startupDir);
  Str1 := startupDir + '/editorONE.ini';
  assignFile(outFile, Str1);
  rewrite(outFile);

  WriteLn(outFile, '<Favourites>');

  submenu := MainMenu1.Items.Find('ProjectMenu');
  for X := 0 to submenu.Count - 1 do
  begin
    Str1 := submenu.Items[X].Caption;
    if (Str1 <> 'SelectRoot') and (Str1 <> '-') then
      WriteLn(outFile, submenu.Items[X].Caption); // END IF
  end // END BEGIN
  ; // END FOR

  WriteLn(outFile, '</Favourites>');

  WriteLn(outFile, '<TabWidth>');
  Str1 := IntToStr(tabWidth);
  WriteLn(outFile, Str1);
  WriteLn(outFile, '</TabWidth>');

  WriteLn(outFile, '<Build>');
  for Y := 0 to 15 do
  begin
    if RunExtGrid.Cells[1, Y] <> '' then
    begin
      Str1 := RunExtGrid.Cells[1, Y];                // Name
      Str2 := RunExtGrid.Cells[2, Y];                // Shortcut
      Str3 := RunExtGrid.Cells[3, Y];                // Working Directory
      Str4 := RunExtGrid.Cells[4, Y];                // Program Name
      Str5 := RunExtGrid.Cells[5, Y];                // Parameters
      WriteLn(outFile, Str1 + ' ' + Str2 + ' ' + Str3 +
        ' ' + Str4 + ' ' + Str5);
    end // END BEGIN
    ; // END IF
  end // BEGIN
  ; // END FOR
  WriteLn(outFile, '</Build>');

  closefile(outFile);

end;

//=====================================================
//  TERMINAL - TerminalIn
//=====================================================
procedure TForm1.TerminalInKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
begin

  if key = VK_RETURN then
  begin

    if cmdsIdx = -1 then
    begin
      cmdsIdx := cmds.Count;    // as this 0 based it will add to the next slot
      cmds.Add(TerminalIn.Text);
    end // END BEGIN
    ; // END IF

    if cmds.Strings[cmdsIdx] <> TerminalIn.Text then
    begin
      cmdsIdx := cmds.Count;    // as this 0 based it will add to the next slot
      cmds.Add(TerminalIn.Text);
    end // END BEGIN
    ; // END IF
    Terminal(cmds.Strings[cmdsIdx]);
    TerminalIn.Text := '';
    exit;
  end // END BEGIN
  ; // END IF

  if key = VK_UP then
  begin
    if cmdsIdx > 0 then
    begin
      Dec(cmdsIdx);
      TerminalIn.Text := cmds.Strings[cmdsIdx];
    end // END BEGIN
    ; // END IF
  end // END BEGIN
  ; // END IF

  if key = VK_DOWN then
  begin
    if cmdsIdx < (cmds.Count - 1) then
    begin
      Inc(cmdsIdx);
      TerminalIn.Text := cmds.Strings[cmdsIdx];
    end // END BEGIN
    ; // END IF
  end // END BEGIN
  ; // END IF

end;


//=====================================================
//  FILEVIEW/CODEVIEW
//=====================================================
procedure TForm1.Left_PageControlChange(Sender: TObject);
begin

  X := Form1.Left_PageControl.ActivePageIndex;
  if X = 0 then
  begin
    Form1.Left_TabSheet1.Font.Color := clYellow;
    Form1.Left_TabSheet2.Font.Color := clDefault;
  end // END BEGIN
  else
  begin
    Form1.Left_TabSheet2.Font.Color := clYellow;
    Form1.Left_TabSheet1.Font.Color := clDefault;
  end // END BEGIN
  ; // END IF

end;

//=====================================================
//  PROJECT MENU
//=====================================================
procedure TForm1.ProjectMenuClick(Sender: TObject);
begin

  Str1 := (Sender as TMenuItem).Caption;

  if Str1 = 'SelectRoot' then
  begin
    Str1 := '';
    if not InputQuery('Change Root', 'Enter the new root', False, Str1) then
      exit;
  end // END BEGIN
  ; // END IF

  if activeTree = 1 then
  begin
    try
      ShellTreeView1.Root := str1;
    except
      on E: EInvalidPath do
      begin
        MessageDlgPos('Invalid Directory', mtConfirmation, [mbCancel], 0, 200, 150);
        exit;
      end // END BEGIN
    end // END TRY
    ; // END TRY
    ShellTreeView1.FileSortType := fstFoldersFirst;
    ShellTreeView1.Refresh();

    treeView1RootNode := ShellTreeView1.Items.GetFirstNode;
    treeView1CurrentNode := treeView1RootNode;
    ShelltreeView1.Select(treeView1RootNode);

  end // END BEGIN
  else
  begin
    try
      ShellTreeView2.Root := str1;
    except
      on E: EInvalidPath do
      begin
        MessageDlgPos('Invalid Directory', mtConfirmation, [mbCancel], 0, 200, 150);
        exit;
      end // END BEGIN
    end // END TRY
    ; // END TRY
    ShellTreeView2.FileSortType := fstFoldersFirst;
    ShellTreeView2.Refresh();

    treeView2RootNode := ShellTreeView2.Items.GetFirstNode;
    treeView2CurrentNode := treeView2RootNode;
    ShelltreeView2.Select(treeView2RootNode);

  end // END BEGIN
  ; // END IF


  Form1.Left_PageControl.ActivePage := Form1.Left_TabSheet1;

end;

//=====================================================
//  FILE MENU
//=====================================================
procedure TForm1.FileMenuClick(Sender: TObject);
var
  key: word;
  Shift: TShiftState;
begin

  Str1 := (Sender as TMenuItem).Caption;
  Shift := [ssCtrl];

  case Str1 of
    'New': key := VK_N;
    'Save': key := VK_S;
    'SaveAs':
    begin
      Shift := [ssCtrl, ssShift];
      key := VK_A;
    end; // END BEGIN
    'Quit': key := VK_Q;
    'Compare Files':
    begin
      Shift := [ssCtrl, ssAlt];
      key := VK_C;
    end; // END BEGIN
  end; // END CASE

  FiredOnce := False;
  FormKeyUp(Sender, key, Shift);
  FiredOnce := True;
  Shortcuts;

end;

//=====================================================
//  VIEW MENU
//=====================================================
procedure TForm1.ToolsMenuClick(Sender: TObject);
var
  key: word;
  Shift: TShiftState;
begin

  Str1 := (Sender as TMenuItem).Caption;
  Shift := [ssAlt];

  case Str1 of
    'Search': key := VK_S;
    'Navigate': key := VK_N;
    'Execute': key := VK_X;
    'Output': key := VK_O;
    'Help': key := VK_H;
  end; // END CASE

  FiredOnce := False;
  FormKeyUp(Sender, Key, Shift);
  FiredOnce := True;

end;

//=====================================================
//  EditMenu MENU
//=====================================================
procedure TForm1.EditMenuClick(Sender: TObject);
var
  key: word;
  Shift: TShiftState;
begin

  Str1 := (Sender as TMenuItem).Caption;
  Shift := [];

  case Str1 of
    'Find':
    begin
      Shift := [ssCtrl];
      key := VK_F;
    end; // END BEGIN
    'FindNext':
    begin
      key := VK_F3;
    end; // END BEGIN
    'FindPrevious':
    begin
      Shift := [ssShift];
      key := VK_F3;
    end; // END BEGIN
    'Bookmark':
    begin
      Shift := [ssCtrl];
      key := VK_B;
    end; //END BEGIN
  end; // END CASE

  FiredOnce := False;
  FormKeyUp(Sender, Key, Shift);
  FiredOnce := True;

end;

//=====================================================
//  M_Help MENU
//=====================================================
procedure TForm1.HelpmenuClick(Sender: TObject);
var
  key: word;
  Shift: TShiftState;
begin

  Str1 := (Sender as TMenuItem).Caption;
  Shift := [ssAlt];

  case Str1 of
    'Help': key := VK_H;
  end; // END CASE

  FiredOnce := False;
  FormKeyUp(Sender, Key, Shift);
  FiredOnce := True;

end;

//=====================================================
//  BROWSER BUTTONS
//=====================================================
procedure TForm1.BrowserButtonsClick(Sender: TObject);
var
  key: word;
  Shift: TShiftState;
begin

  if (Sender is TButton) then
    Str1 := (Sender as TButton).Caption; // END IF
  if (Sender is TMenuItem) then
    Str1 := (Sender as TMenuItem).Caption; // END IF

  Shift := [];

  case Str1 of
    'Edit': key := VK_F4;
    'Copy': key := VK_F5;
    'Move': key := VK_F6;
    'MkDir': key := VK_F7;
    'Delete': key := VK_F8;
  end; // END CASE

  FiredOnce := False;
  FormKeyUp(Sender, Key, Shift);
  FiredOnce := True;

end;

//-----------------------------------------------------
// GOTO
//-----------------------------------------------------
procedure TForm1.B_JumpClick(Sender: TObject);
var
  key: word;
  Shift: TShiftState;
begin

  key := VK_RETURN;
  Shift := [];

  FiredOnce := False;
  FormKeyUp(Sender, Key, Shift);
  FiredOnce := True;

end;

//-----------------------------------------------------
//  FIND
//-----------------------------------------------------

//FIND/REPLACE BUTTON CLICK

procedure TForm1.FindReplaceClick(Sender: TObject);
var
  key: word;
  Shift: TShiftState;
begin

  Str1 := (Sender as TButton).Caption;
  Shift := [];

  case Str1 of
    'Find', 'Replace', 'ReplaceAll': key := VK_F3;
  end; // END CASE

  FiredOnce := False;
  FormKeyUp(Sender, Key, Shift);
  FiredOnce := True;

  Tools;

end;

// FIND

procedure Find(Sender: TObject);
var
  opts: TSynSearchOptions;
begin
  opts := [ssoFindContinue];
  if Form1.CheckBox1_CaseSensitive.Checked = True then
    opts := opts + [ssoMatchCase];
  if Form1.CheckBox2_WholeWord.Checked = True then
    opts := opts + [ssoWholeWord];
  if Form1.CheckBox3_PromptOnReplace.Checked = True then
    opts := opts + [ssoPrompt];
  if Form1.RadioButton2_Backward.Checked = True then
    opts := opts + [ssoBackwards];
  if Sender = Form1.B_Replace then
    opts := [ssoReplace];
  if Sender = Form1.B_ReplaceAll then
    opts := opts + [ssoReplaceAll];

  X := FocusedSynEdit.SearchReplace(Form1.T_Search.Text, Form1.T_Replace.Text, opts);
  Form1.Edit_PageControl.SetFocus();

end;

{ ============================================================================= }
{ on form key up check for function keys                                         }
{ NOTE: All functions are dealt with her                                        }
{   That means Menu Clicks/Button Press's are setup the call to this            }
{ ============================================================================= }
procedure TForm1.FormKeyUp(Sender: TObject; var Key: word; Shift: TShiftState);
begin

  if not validOption(key, Shift) then
  	begin
  	  key := 0;
			exit;
  	end // END BEGIN
  ; // END IF

  {  ----------------- }
  {  SynEdit functions }
  {  ----------------- }
  //ecDuplicateLine
  if not (ssAlt in Shift) and not (ssShift in Shift) and (ssCtrl in Shift) and (key = VK_D) then
  begin
    FocusedSynEdit.ExecuteCommand(632, '', nil);
    exit;
  end // END BEGIN
  ; // END IF

  //eoShowSpecialChars
  if not (ssAlt in Shift) and not (ssShift in Shift) and (ssCtrl in Shift) and (key = VK_W) then
  begin
    if whitespace = False then
    begin
      whitespace := True;
      FocusedSynEdit.Options := FocusedSynEdit.Options + [eoShowSpecialChars];
      exit;
    end // END BEGIN
    else
    begin
      whitespace := False;
      FocusedSynEdit.Options := FocusedSynEdit.Options - [eoShowSpecialChars];
      exit;
    end; // END BEGIN
  end // END BEGIN
  ; // END IF

  {  ------------- }
  {  PF1 - Project }
  {  ------------- }
  if (key = VK_F1) then
  begin
    Form1.Left_PageControl.ActivePage := Left_TabSheet1;
    Form1.Left_PageControlChange(Form1.Left_PageControl);
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  {  -------------- }
  {  PF2 - CodeTree }
  {  -------------- }
  if (key = VK_F2) then
  begin
    Form1.Left_PageControl.ActivePage := Left_TabSheet2;
    Form1.Left_PageControlChange(Form1.Left_PageControl);
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  //FILES

  {  -----------------  }
  {  Ctrl N [NewFile]  }
  {  -----------------  }
  if not (ssAlt in Shift) and not (ssShift in Shift) and (ssCtrl in Shift) and (key = VK_N) then
  begin
    fullPathSrc := ShellTreeView2.Path + 'NEW.TXT';
    activeTree := 3;
    NewSynEdit;
    activeTree := 3;
    files[filesIdx].Name := fullPathSrc;
    Form1.Left_PageControl.ActivePage := Form1.Left_TabSheet2;
    FocusedSynEdit.SetFocus;
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  {  --------------------  }
  {  Ctrl/Shift-A [SaveAs] }
  {  --------------------  }
  if not (ssAlt in Shift) and (ssCtrl in Shift) and (ssShift in Shift) and (key = VK_A) then
  begin
    SaveFileAs;
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  {  ---------------  }
  {  Ctrl-S [Save]    }
  {  ---------------  }
  if not (ssAlt in Shift) and not (ssShift in Shift) and (ssCtrl in Shift) and (key = VK_S) then
  begin
    SaveFile;
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  {  ---------------  }
  {  Ctrl-Q [Quit]    }
  {  ---------------  }
  if not (ssAlt in Shift) and not (ssShift in Shift) and (ssCtrl in Shift) and (key = VK_Q) then
  begin
    QuitFile;
    if filesIdx < 0 then
      Form1.Left_PageControl.ActivePage := Left_TabSheet1
    else
      Form1.Left_PageControl.ActivePage := Left_TabSheet2;
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  //NO MENU OPTIONS ON THESE

  {  -----------------------------------  }
  {  Ctrl-G M_Tools = Jump of M_Navigate  }
  {  -----------------------------------  }
  if not (ssAlt in Shift) and not (ssShift in Shift) and (ssCtrl in Shift) and (key = VK_G) then
  begin
    toolsYes := False;
    Tools;
    if toolsYes = True then
    begin
      Tools_PageControl.ActivePage := Tools_Navigate;
      T_Jump_LineNo.SetFocus;
    end // END BEGIN
    ; // END IF
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  {  -------------------- }
  {  Allow ENTER for Jump }
  {  -------------------- }
  if key = VK_RETURN then                        // Allow ENTER on jump screen
  begin
    X := Tools_PageControl.TabIndex;
    if (toolsYes = True) and (X = 1) then
    begin
      Tools;
      Y := StrToInt(T_Jump_LineNo.Text);
      FocusedSynEdit.TopLine := Y;
      key := 0;
      exit;
    end // END CASE
    ; // END IF
  end // END CASE
  ; // END IF

  {  ----------------  }
  {  Ctrl-B [Bookmark]  }
  {  ----------------  }
  if not (ssAlt in Shift) and not (ssShift in Shift) and (ssCtrl in Shift) and (key = VK_B) then
  begin
    AddBookmark(Sender);
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  {  ------------------  }
  {  Ctrl-U [Uppercase]  }
  {  ------------------  }
  if not (ssAlt in Shift) and not (ssShift in Shift) and (ssCtrl in Shift) and (key = VK_U) then
  begin
    FocusedSynEdit.SelText := UpperCase(FocusedSynEdit.SelText);
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  {  ------------------  }
  {  Ctrl-L [Lowercase]  }
  {  ------------------  }
  if not (ssAlt in Shift) and not (ssShift in Shift) and (ssCtrl in Shift) and (key = VK_L) then
  begin
    FocusedSynEdit.SelText := LowerCase(FocusedSynEdit.SelText);
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  {  ------------------  }
  {  Alt-Shift-S [Sort]  }
  {  ------------------  }
  if not (ssCtrl in Shift) and (ssAlt in Shift) and (ssShift in Shift) and (key = VK_S) then
  begin
    temp := TStringList.Create;
    temp.Text := FocusedSynEdit.Text;
    temp.Sort;
    FocusedSynEdit.Text := temp.Text;
    temp.Free;
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF


  //EDITOR

  {  ------------  }
  {  Ctrl-F M_Find }
  {  ------------- }
  if not (ssAlt in Shift) and not (ssShift in Shift) and (ssCtrl in Shift) and (key = VK_F) then
  begin
    toolsYes := False;
    Tools;
    if toolsYes = True then
    begin
      Tools_PageControl.ActivePage := Tools_Find;
      T_Search.Text := FocusedSynEdit.SelText;
      T_Search.SetFocus;
    end; // END IF
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  {  ------------------------  }
  {  Shift-F3 [M_FindPrevious] }
  {  ------------------------  }
  if not (ssAlt in Shift) and not (ssCtrl in Shift) and (ssShift in Shift) and (key = VK_F3) then
  begin
    RadioButton2_Backward.Checked := True;
    Find(Sender);
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  {  ------------------- }
  {   M_Find using ENTER }
  {  ------------------- }
  if key = VK_RETURN then                        // Allow ENTER on search screen
  begin
    X := Tools_PageControl.TabIndex;
    if (toolsYes = True) and (X = 0) then
    begin
      Tools;
      key := VK_F3;
    end // END CASE
    ; // END IF
  end // END CASE
  ; // END IF

  if key = VK_F3 then
  begin
    RadioButton1_Forward.Checked := True;
    Find(Sender);
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  //TOOLS

  {  ---------------  }
  {  Alt-S  = M_Find  }
  {  ---------------  }
  if not (ssCtrl in Shift) and not (ssShift in Shift) and (ssAlt in Shift) and (key = VK_S) then
  begin
    toolsYes := False;
    Tools;
    if toolsYes = True then
    begin
      Tools_PageControl.ActivePage := Tools_Find;
      T_Search.Text := FocusedSynEdit.SelText;
      T_Search.SetFocus;
    end; // END IF
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  {  ---------------------------  }
  {  Alt-N  M_Tools = M_Navigate  }
  {  ---------------------------  }
  if not (ssCtrl in Shift) and not (ssShift in Shift) and (ssAlt in Shift) and (key = VK_N) then
  begin
    toolsYes := False;
    Tools;
    if toolsYes = True then
    begin
  		Str1 := InTToStr(FocusedSynEdit.CaretX);
  		Str2 := IntToStr(FocusedSynEdit.CaretY);
			CursorPos.Caption := 'Current Row=' + Str2 + ' Col=' + Str1;
      Tools_PageControl.ActivePage := Tools_Navigate;
      T_Jump_LineNo.SetFocus;
    end // END BEGIN
    ; // END IF
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  {  ----------------  }
  {  Alt-X  M_Execute  }
  {  ----------------  }
  if not (ssCtrl in Shift) and not (ssShift in Shift) and (ssAlt in Shift) and (key = VK_X) then
  begin
    toolsYes := False;
    Tools;
    if toolsYes = True then
    begin
      Tools_PageControl.ActivePage := Tools_Execute;
    end // END BEGIN
    ; // END IF
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  {  ---------------  }
  {  Alt-O  M_Output  }
  {  ---------------  }
  if not (ssCtrl in Shift) and not (ssShift in Shift) and (ssAlt in Shift) and (key = VK_O) then
  begin
    toolsYes := False;
    Tools;
    if toolsYes = True then
    begin
      Tools_PageControl.ActivePage := Tools_Output;
    end // END BEGIN
    ; // END IF
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  {  -------------  }
  {  Alt-H  M_Help  }
  {  -------------  }
  if not (ssCtrl in Shift) and not (ssShift in Shift) and (ssAlt in Shift) and (key = VK_H) then
  begin
    toolsYes := False;
    Tools;
    if toolsYes = True then
    begin
      Tools_PageControl.ActivePage := Tools_Help;
    end // END BEGIN
    ; // END IF
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  //BROWSER

  {  -------------- }
  {  PF4 - EditMenu }
  {  -------------- }
  if (key = VK_F4) then
  begin
    x_Edit;
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  {  ------------ }
  {  PF5 - Copy   }
  {  ------------ }
  if (key = VK_F5) then
  begin
    x_Copy;
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  {  ------------ }
  {  PF6 - Move   }
  {  ------------ }
  if (key = VK_F6) then
  begin
    x_Move;
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  {  ------------ }
  {  PF7 - MkDir   }
  {  ------------ }
  if (key = VK_F7) then
  begin
    x_MkDir(Sender);
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  {  ------------ }
  {  PF8 - Delete }
  {  ------------ }
  if (key = VK_F8) then
  begin
    x_Delete;
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  //CODETREE

  {  ------------------------------ }
  {  Ctrl-Shift-T - Reload CodeTree }
  {  ------------------------------ }
  if not (ssAlt in Shift) and (ssCtrl in Shift) and (ssShift in Shift) and (key = VK_T) then
  begin
    currentTab := Form1.Edit_PageControl.ActivePageIndex;
    CodeTreeView(files[currentTab].Name);
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  //VTERMINAL

  {  ----------------- }
  {  Alt-T - VTerminal }
  {  ----------------- }
  if not (ssCtrl in Shift) and not (ssShift in Shift) and (ssAlt in Shift) and (key = VK_T) then
  begin
    if Form1.Terminal1.Visible then
    begin
      Form1.TerminalOut.Visible := True;
  			GetDir(0, pwd);
  			Form1.TerminalOut.Lines.Add(pwd);
      Form1.TerminalIn.Visible := True;
      Form1.Terminal1.Visible := False;
      Form1.TerminalIn.SetFocus;
    end // END CASE
    else
    begin
      Form1.TerminalOut.Visible := False;
      Form1.TerminalIn.Visible := False;
      Form1.Terminal1.Visible := True;
    end // END CASE
    ; // END IF
    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

  {  ---------------------------- }
  {  Ctrl-Alt-C - Compare Files		}
  {  ---------------------------- }
  if not (ssShift in Shift) and (ssCtrl in Shift) and (ssAlt in Shift) and (key = VK_C) then
  begin

		If (SynEdit0 <> nil) and (SynEdit1 <> nil) then
		begin
  		X := SynEdit0.TopLine;
  		Y := SynEdit1.TopLine;
	  	repeat
	  		begin
  	  		Str1 := SynEdit0.Lines[X];
    			Str2 := SynEdit1.Lines[Y];
					If Str1 <> Str2 then
						begin
        			MessageDlgPos('File 1 and File 2 differ at line ' + IntToStr(X+1), mtConfirmation, [mbCancel], 0, 200, 150);
  						SynEdit0.TopLine := X+1;
  						SynEdit1.TopLine := Y+1;
							Exit;
						end // END BEGIN
					; // END IF
					inc(X);
					inc(Y);
				end
			until X > SynEdit0.Lines.Count;
		end // END BEGIN
		; // END IF

    key := 0;
    exit;
  end // END BEGIN
  ; // END IF

end;



function validOption(Key: word; Shift: TShiftState): boolean;
begin

  {  ----------------------- }
  {  ESC Tools                }
  {  ----------------------- }
  if (key = VK_ESCAPE) and (toolsYes = True) then
  begin
    Tools;
    key := 0;
    Result := False;
    exit;
  end // END BEGIN
  ; // END IF

  {  ----------------------------- }
  {  Fires twice if Menu Shortcuts  }
  {  ----------------------------- }
  if FiredOnce then
  begin
    FiredOnce := False;
    key := 0;
    Result := False;
    exit;
  end // END BEGIN
  ; // END IF

  Result := True;
  Form1.Tools_Find.TabVisible := True;
  Form1.Tools_Navigate.TabVisible := True;
  Form1.Tools_Execute.TabVisible := True;
  Form1.Tools_Output.TabVisible := True;
  Form1.Tools_Help.TabVisible := True;

  if Form1.Edit_PageControl.ActivePageIndex > -1 then
    exit
  else
    case key of
      VK_F4..VK_F8: exit;
      VK_H: exit;
      VK_N:
        if ssCtrl in Shift then
          exit;
      VK_T:
        if ssAlt in Shift then
          exit;
    end // END CASE
  ; // END IF

  Result := False;
  Form1.Tools_Find.TabVisible := False;
  Form1.Tools_Navigate.TabVisible := False;
  Form1.Tools_Execute.TabVisible := False;
  Form1.Tools_Output.TabVisible := False;

end;

//=====================================================
//  SYN EditMenu
//=====================================================
procedure TForm1.Edit_PageControlChange(Sender: TObject);
var
  ActiveTab: TTabSheet;
  CurrentTabSheet: TTabSheet;

  key: word;
  Shift: TShiftState;
begin
  //This will fire AFTER the 2nd->nth  is created so we
  //need to make sure that that we dont do it again
  //FOR JUST A CLICK THEN I WILL CONTAIN THE ACTIVE TAB
  //Will also fire AFTER a Tab is deleted

  //----------------------------------------------------
  //Set all the TAB names to their default color
  //and then change the active one to Yellow
  for X := 0 to Edit_PageControl.PageCount - 1 do
  begin
    currentTabSheet := Edit_PageControl.Pages[X];
    currentTabSheet.Font.Color := clDefault;
  end; // END FOR
  Edit_PageControl.ActivePage.Font.Color := clYellow;
  //----------------------------------------------------

  //A delete will fire before this on so if it has we dont want to do anymore
  if deletedIdx > -1 then
  begin
    exit;
  end // END CASE
  ; // END IF

  //On a change the Active Tab will already have been set
  //So we select the Focused editor from it
  X := Form1.Edit_PageControl.ActivePageIndex;
  currentTab := X;
  ActiveTab := Edit_PageControl.ActivePage;
  FocusedSynEdit := TSynEdit(ActiveTab.Components[0]);

  Str1 := files[currentTab].Name;
  CodeTreeView(Str1);

  key := VK_F2;
  Shift := [];
  FormKeyUp(Sender, key, Shift);

end;

procedure TForm1.Edit_PageControlCloseTabClicked(Sender: TObject);
var
  ClosedTab: TTabSheet;
  key: word;
  Shift: TShiftState;
begin
  ClosedTab := (Sender as TTabSheet);
  deletedIdx := ClosedTab.PageIndex;
  Form1.Edit_PageControl.ActivePageIndex := deletedIdx;
  // This fires a PageControlChange

  key := 81;
  Shift := [ssCtrl];
  firedOnce := False;
  FormKeyUp(Sender, key, Shift);
  Shortcuts;

end;

//=====================================================
//  CODE VIEW
//=====================================================

// DBL CLICK

procedure TForm1.CodeViewDblClick(Sender: TObject);
var
  MousePos: TPoint;
begin
  MousePos := Mouse.CursorPos;
  MousePos := CodeView.ScreenToClient(MousePos);
  saveNode := CodeView.GetNodeAt(MousePos.X, MousePos.Y);
  if saveNode = nil then
    exit;

  str1 := MidStr(TTreeView(Sender).Selected.Text, 1, 5);
  Y := StrToInt(str1);
  FocusedSynEdit.TopLine := Y;
  FocusedSynEdit.CaretY := Y;

  FocusedSynEdit.SetFocus;

end;

//=====================================================
//  TOOLS from View Menu
//=====================================================
procedure Tools();
begin

  if ToolsYes = True then
  begin
    ToolsYes := False;
    Form1.PanelB.Anchors := [akTop, akRight, akLeft, akBottom];
    Form1.PanelB.Height := 0;          // Cuz they are now anchored
    Form1.PanelC.Anchors := [akTop, akRight, akLeft, akBottom];
    Form1.PanelC.Height := 0;          // Cuz they are now anchored
    Form1.PanelD.Visible := False;
    if Form1.Edit_PageControl.ActivePageIndex > -1 then
      FocusedSynEdit.SetFocus;
  end // END BEGIN
  else
  begin
    ToolsYes := True;
    Form1.PanelB.Anchors := [akTop, akRight, akLeft];
    Form1.PanelB.Height := 710;
    Form1.PanelC.Anchors := [akTop, akRight, akLeft];
    Form1.PanelC.Height := 710;
    Form1.PanelD.Visible := True;
  end // END BEGIN
  ; // END IF

end;

//=====================================================
//  FILE Functions from Files Menu
//=====================================================

//SAVE

procedure SaveFile();
begin

  Str1 := files[Form1.Edit_PageControl.ActivePageIndex].Name;

  if ExtractFileName(Str1) = 'NEW.TXT' then
    SaveFileAs
  else
    FocusedSynEdit.Lines.SaveToFile(str1);

end;

//SAVE AS

procedure SaveFileAs();
begin

  Str1 := files[Form1.Edit_PageControl.ActivePageIndex].Name;
  if InputQuery('Save As', 'Save File As', False, str1) then
    FocusedSynEdit.Lines.SaveToFile(str1);

end;

//QUIT

procedure QuitFile();
var
  ActiveTab: TTabSheet;
begin

  case MessageDlgPos('File has not been save - do you still want to quit',
      mtConfirmation, [mbYes, mbCancel], 0, 200, 200) of
    mrCancel: Exit;
    mrYes:
    begin
      shuffleFiles;
      Form1.Edit_PageControl.ActivePage.Destroy;
      deletedIdx := -1;
      if filesIdx = -1 then
      begin
        Form1.CodeView.Items.Clear;
        Form1.CodeView.Items.Add(nil, str1);
        exit;
      end // END BEGIN
      ; // END IF
    end // END BEGIN
  end; // END CASE

  X := Form1.Edit_PageControl.ActivePageIndex;

  //On a change the Active Tab will already have been set
  //So we select the Focused editor from it
  ActiveTab := Form1.Edit_PageControl.ActivePage;
  FocusedSynEdit := TSynEdit(ActiveTab.Components[0]);

  filesIdx := X;
  currentTab := X;
  Str1 := files[currentTab].Name;
  CodeTreeView(Str1);

end;

procedure shuffleFiles();
var
  src: int64;
  dst: int64;
begin

  //  Don't forget at this point fileNamesIdx has already been decremented

  dst := Form1.Edit_PageControl.ActivePageIndex;    // This what has just been deleted
  src := dst + 1;

  while not (src > filesIdx) do
  begin
    files[dst] := files[src];
    files[src].Name := '';
    files[src].codeTree := initCodeTree;
    files[src].codeTreeIdx := -1;
    Inc(src);
    Inc(dst);
  end // END CASE
  ; // END WHILE

  Dec(filesIdx);
  currentTab := filesIdx;

end;

//-----------------------------------------------------
// BOOKMARKS
//-----------------------------------------------------
procedure AddBookmark(Sender: TObject);
begin

  Y := FocusedSynEdit.CaretY;
  str2 := FocusedSynEdit.Lines[Y - 1];
  if not InputQuery('Add Bookmark', 'Enter a comment for this Bookmark', False, str2) then
    exit;

  Inc(bookmarkRow);
  str1 := IntToStr(Y);
  Form1.BookmarkGrid.InsertRowWithValues(bookmarkRow, ['', Str1, str2]);

end;


//=====================================================
// BROWSER
//=====================================================

// EDIT

procedure x_Edit();
begin
  //  If ShellNode.IsDirectory then
  //    begin
  //      MessageDlgPos('Cannot edit a directory/unselected item', mtConfirmation, [mbCancel],0,200,200);
  //      exit;
  //    end // END BEGIN
  //  ; // END IF

  if filesIdx > 14 then
  begin
    MessageDlgPos('Maximum No of Tabs is 16 - Sorry', mtConfirmation,
      [mbCancel], 0, 200, 200);
    exit;
  end // END BEGIN
  ; // END IF

  NewSynEdit;

  if activeTree = 1 then
  begin
    fullPathSrc := Form1.ShellTreeView1.Path;
    if RightStr(fullPathsrc, 1) <> '/' then
    try
      FocusedSynEdit.Lines.LoadFromFile(Form1.ShellTreeView1.Path)
    except
      exit
    end; // END TRY
    ; // END IF
  end // END BEGIN
  ; // END IF

  if activeTree = 2 then
  begin
    fullPathSrc := Form1.ShellTreeView2.Path;
    if RightStr(fullPathsrc, 1) <> '/' then
    try
      FocusedSynEdit.Lines.LoadFromFile(Form1.ShellTreeView2.Path)
    except
      exit
    end; // END TRY
    ; // END IF
  end // END BEGIN
  ; // END IF

  files[filesIdx].Name := fullPathSrc;
  CodeTreeView(FullPathSrc);
  FocusedSynEdit.SetFocus;
  Shortcuts;

end;

//CREATE NEW SYNEDIT

procedure NewSynEdit();
var

  NewTab: TTabSheet;
  NewSynAnySyn: TSynAnySyn;
  NewRuler: TECRuler;
  init: boolean = True;
  G: double;

begin

  //NEW TAB

  Inc(filesIdx);
  currentTab := filesIdx;

  NewTab := TTabSheet.Create(Form1.Edit_PageControl);
  NewTab.PageControl := Form1.Edit_PageControl;

  case activeTree of
    1: begin
      Str1 := ExtractFileExt(Form1.ShellTreeView1.Path);
      NewTab.Caption := ExtractFileName(Form1.ShellTreeView1.Path);
    end; // END BEGIN
    2: begin
      Str1 := ExtractFileExt(Form1.ShellTreeView2.Path);
      NewTab.Caption := ExtractFileName(Form1.ShellTreeView2.Path);
    end; // END BEGIN
    3: begin
      Str1 := fullPathSrc;
      NewTab.Caption := 'NEW.TXT';
    end; // END BEGIN
  end; // END CASE

  //NEW SYNEDIT

  FocusedSynEdit := TSynEdit.Create(NewTab);

  FocusedSynEdit.Parent := NewTab;
  FocusedSynEdit.Align := alClient;
  FocusedSynEdit.Anchors := [];
  FocusedSynEdit.BlockIndent := 2;
  FocusedSynEdit.Color := clGradientInactiveCaption;
  FocusedSynEdit.Font.Height := -11;
  FocusedSynEdit.Font.Color := clAqua;
	FocusedSynEdit.RightEdge := 8192;

  FocusedSynEdit.Options := FocusedSynEdit.Options - [eoScrollPastEol];
  FocusedSynEdit.Options := FocusedSynEdit.Options - [eoTabsToSpaces];
  FocusedSynEdit.Options := FocusedSynEdit.Options - [eoTabIndent];
  FocusedSynEdit.Options := FocusedSynEdit.Options - [eoSmartTabs];
  FocusedSynEdit.TabWidth := tabWidth;

  FocusedSynEdit.Highlighter := TSynAnySyn.Create(FocusedSynEdit);
  NewSynAnySyn := TSynAnySyn.Create(FocusedSynEdit);
  FocusedSynEdit.Highlighter := NewSynAnySyn;
  Highlight(NewSynAnySyn);

  //ACTIVATE IT
  Form1.Edit_PageControl.ActivePage := NewTab;
  whitespace := False;

	If Form1.Edit_PageControl.ActivePageIndex = 0 then
		SynEdit0 := FocusedSynEdit;
	If Form1.Edit_PageControl.ActivePageIndex = 1 then
		SynEdit1 := FocusedSynEdit;

  //RULER
  if init = True then
  begin
    NewRuler := TECRuler.Create(NewTab);
    NewRuler.Parent := NewTab;
    NewRuler.Align := alTop;
    NewRuler.Width := 762;
    NewRuler.Constraints.MaxHeight := 35;
    NewRuler.IndentTopLeft := 72;
    NewRuler.PointerMode := epmNone;
    init := False;
  end // END CASE
  ;  // END IF

end;

//HIGHLIGHT

procedure Highlight(ASynAnySyn: TSynAnySyn);
var

  C: TColor;
  S: TFontStyles;
begin

  //Str1:=GetCurrentDir;
  Str1 := startupDir + '/Highlight' + Str1;
  assignFile(inFile, Str1);
  {$I-}
  reset(inFile);
  {$I+}
  X := IOResult;
  if X <> 0 then
    exit;

  files[filesIdx].codeTreeIdx := -1;

  readLn(inFile, Str1);
  Str1 := PChar(Str1.TrimLeft);
  UStr := UpperCase(Str1);

  while not EOF(inFile) do
  begin

    if UStr = '<CODETREE>' then
    begin
      readLn(inFile, Str1);
      Str1 := PChar(Str1.TrimLeft);
      while not EOF(inFile) do
      begin
        if LeftStr(Trim(Str1), 2) = '</' then break;

        Inc(files[filesIdx].codeTreeIdx);
        if files[filesIdx].codeTreeIdx > 15 then
        begin
          MessageDlgPos('Maximum No of CodeTree items - Sorry', mtConfirmation,
            [mbCancel], 0, 200, 200);
          exit;
        end // END BEGIN
        ; // END IF
        files[filesIdx].codeTree[files[filesIdx].codeTreeIdx] := UpperCase(Str1);
        readLn(inFile, Str1);
        Str1 := PChar(Str1.TrimLeft);
      end; // END WHILE
    end // END BEGIN
    ; // END IF

    if UStr = '<STRINGDELIMITER>' then
    begin
      readLn(inFile, Str1);
      Str1 := PChar(Str1.TrimLeft);
      case Str1 of
        'sdSingleQuote': ASynAnySyn.StringDelim := sdSingleQuote;
        'sdDoubleQuote': ASynAnySyn.StringDelim := sdDoubleQuote;
      end; // END CASE
    end // END BEGIN
    ; // END IF

    if UStr = '<COMMENTSTYLE>' then
    begin
      readLn(inFile, Str1);
      Str1 := PChar(Str1.TrimLeft);
      case Str1 of
        'csAsmStyle': ASynAnySyn.Comments := [csAsmStyle];
      end; // END CASE
    end // END BEGIN
    ; // END IF

    if UStr = '<FOREGROUNDCOLOURS>' then
    begin
      readLn(inFile, Str1);
      Str1 := PChar(Str1.TrimLeft);
      while not EOF(inFile) do
      begin
        if LeftStr(Trim(Str1), 2) = '</' then break;

        X := Pos('=', Str1, 1);
        Str2 := UpperCase(Trim(MidStr(Str1, 1, X - 1)));
        Str3 := Trim(MidStr(Str1, X + 1, 99));
        C := StringToColor(Str3);

        case Str2 of
          'STRING': ASynAnySyn.StringAttri.Foreground := C;
          'NUMBER': ASynAnySyn.NumberAttri.Foreground := C;
          'SYMBOL': ASynAnySyn.SymbolAttri.Foreground := C;
          'COMMENT': ASynAnySyn.CommentAttri.Foreground := C;
          'IDENTIFIER': ASynAnySyn.IdentifierAttri.Foreground := C;
          'CONSTANT': ASynAnySyn.ConstantAttri.Foreground := C;
          'KEYWORD': ASynAnySyn.KeyAttri.Foreground := C;
        end; // END CASE

        readLn(inFile, Str1);
        Str1 := PChar(Str1.TrimLeft);

      end; // END WHILE
    end // END BEGIN
    ; // END IF

    if UStr = '<STYLES>' then
    begin
      readLn(inFile, Str1);
      Str1 := PChar(Str1.TrimLeft);

      while not EOF(inFile) do
      begin
        if LeftStr(Trim(Str1), 2) = '</' then break;

        S := [];
        X := Pos('fsBold', Str1, 1);
        if X > 0 then S := [fsBold];
        X := Pos('fsItalic', Str1, 1);
        if X > 0 then S := S + [fsBold];
        X := Pos('fsUnderline', Str1, 1);
        if X > 0 then S := S + [fsUnderline];
        X := Pos('fsStrikeOut', Str1, 1);
        if X > 0 then S := S + [fsStrikeOut];

        X := Pos('=', Str1, 1);
        Str2 := UpperCase(Trim(MidStr(Str1, 1, X - 1)));

        case Str2 of
          'STRING': ASynAnySyn.StringAttri.Style := S;
          'NUMBER': ASynAnySyn.NumberAttri.Style := S;
          'SYMBOL': ASynAnySyn.SymbolAttri.Style := S;
          'COMMENT': ASynAnySyn.CommentAttri.Style := S;
          'IDENTIFIER': ASynAnySyn.IdentifierAttri.Style := S;
          'CONSTANT': ASynAnySyn.ConstantAttri.Style := S;
          'KEYWORD': ASynAnySyn.KeyAttri.Style := S;
        end; // END CASE

        readLn(inFile, Str1);
        Str1 := PChar(Str1.TrimLeft);

      end; // END WHILE
    end // END BEGIN
    ; // END IF

    if UStr = '<CONSTANTS>' then
    begin
      readLn(inFile, Str1);
      Str1 := PChar(Str1.TrimLeft);
      while not EOF(inFile) do
      begin
        if LeftStr(Str1, 2) = '</' then break;
        ASynAnySyn.Constants.Add(Str1);
        readLn(inFile, Str1);
        Str1 := PChar(Str1.TrimLeft);
      end; // END WHILE
    end // END BEGIN
    ; // END IF

    if UStr = '<KEYWORDS>' then
    begin
      readLn(inFile, Str1);
      Str1 := PChar(Str1.TrimLeft);
      while not EOF(inFile) do
      begin
        if LeftStr(Str1, 2) = '</' then break;
        ASynAnySyn.Keywords.Add(Str1);
        readLn(inFile, Str1);
        Str1 := PChar(Str1.TrimLeft);
      end; // END WHILE
    end // END BEGIN
    ; // END IF

    readLn(inFile, Str1);
    Str1 := PChar(Str1.TrimLeft);
    UStr := UpperCase(Str1);

  end; // END WHILE

  closefile(inFile);

end;

//TREE VIEW

procedure CodeTreeView(Str1: string);
//  We have defined in the .ini file the entries that represent the CodeTree
//    ie BEGIN.SUB BEGIN.FUNCTION
//  by coding standards we will prefix each routine with a value that
//    represents where it belongs in the program ie.
//      A1_    Calls A11_ & A12_
//        A11_  Calls A111_ & A112_
//           A111_  Calls no others
//           A112_ Calls no others
//       A12_ Calls no others

//  by locating position of the LAST number we can then Add it as a child to
//  the previous node (Stored in an array of 16
var

  ParentNodes: array[0..15] of TTreeNode;
  Idx: int64;
begin

  Form1.CodeView.Items.Clear;                                    // Clear the CodeTree
  for X := 0 to 15 do                                            // and the Node Array
    ParentNodes[X] := nil;

  Idx := 0;                                                      // Initialise the Idx
  ParentNodes[Idx] := Form1.CodeView.Items.Add(nil, Str1);        // and get the root

  for X := 0 to FocusedSynEdit.Lines.Count - 1 do
    // Moving through each line of our program
  begin                                                        // BEGIN
    Str1 := UPCASE(FocusedSynEdit.Lines[X].TrimLeft);
    // get an Upper Case copy with no leading whitespace
    for Y := 0 to files[currentTab].codeTreeIdx do
      // Now iterate through the CodeTree table
    begin                                                    // BEGIN
      if Pos(files[currentTab].codeTree[Y], Str1, 1) > 0 then  // If we find a match
      begin                                                // BEGIN - building and entry in the CodeTree
        Str1 := Format('%0.5d', [X + 1]) + ' ' + Str1;
        // prefix the entry with the line No

        W := Pos(' ', Str1, 7);                              // Step past that number line number
        if W = 0 then                                      // If we have no space (ie 'Loop:' in ASM
        begin                                            // BEGIN
          Form1.CodeView.Items.AddChild(ParentNodes[0], Str1);  // Just hangs off the root
          break;                                              // break
        end // END CASE
        ; // END IF

        Str2 := MidStr(Str1, W + 2, length(Str1));
        // routines prefix (less the 1st char) ie B11_ we get 11_
        for W := 1 to length(Str2) do                      // Now we look for the last digit
          if not (Str2[W] in ['0'..'9']) then              // If its not a digit
          begin                                          // BEGIN
            if W = 1 then                               // If 1st character then there are no digits
            begin                                      // BEGIN
              Form1.CodeView.Items.AddChild(ParentNodes[0], Str1);
              // Just hangs off the root
              break;                                              // break
            end // END CASE
            else                                        // ELSE
            begin                                      // BEGIN
              Idx := W - 2;                            // the Parent is 2 back from the last digit
              if ParentNodes[Idx] = nil then
                // If it has no Parent (//02_1_DISPLAY or the like)
                Form1.CodeView.Items.AddChild(ParentNodes[0], Str1)
              // Just hangs off the root
              else                                    // ELSE
                ParentNodes[Idx + 1]                    // Add the child
                := Form1.CodeView.Items.AddChild(ParentNodes[Idx], Str1);
              // END IF
              break;
            end // END CASE
            ; // END IF
          end // END BEGIN
        ; // END IF
        ; // END FOR

      end // END BEGIN
      ; // END FOR
    end // END BEGIN
    ; // END FOR
  end // END BEGIN
  ; // END IF

  ParentNodes[0].Expand(True);

  Form1.Left_PageControl.ActivePage := Form1.Left_TabSheet2;
  Form1.Left_PageControlChange(Form1.Left_PageControl);

end;

// COPY - Set it up

procedure x_Copy();
begin

  if activeTree = 1 then
  begin
    fullPathSrc := Form1.ShellTreeView1.Path;
    fullPathDst := Form1.ShellTreeView2.Path;
    dir2Empty := IsDirectoryEmpty(ExtractFilePath(Form1.ShellTreeView2.Path));
    //      Y := 2;
  end
  else
  begin
    fullPathSrc := Form1.ShellTreeView2.Path;
    fullPathDst := Form1.ShellTreeView1.Path;
    dir1Empty := IsDirectoryEmpty(ExtractFilePath(Form1.ShellTreeView1.Path));
    //      Y := 1;
  end;  // ENDIF

  if (RightStr(fullPathSrc, 1) = '/') and (RightStr(fullPathDst, 1) = '/') then
  begin
    Y := Length(fullPathSrc) - 1;
    for Y := Y downto 0 do
      if MidStr(fullPathSrc, Y, 1) = '/' then break;
    ; // END FOR
    Inc(Y);
    fullPathDst := fullPathDst + MidStr(fullPathSrc, Y, 254);
    copyDirectory(fullPathSrc, fullPathDst);
  end // END BEGIN
  else
  if RightStr(fullPathSrc, 1) <> '/' then
    fileCopy
  else
  begin
    MessageDlgPos('Invalid Copy Options', mtConfirmation, [mbOK], 0, 200, 200);
    exit;
  end // END BEGIN
  ; // END IF
  ; // END IF

  if (activeTree = 1) then
    if dir2Empty then
    begin
      saveNode := treeView2CurrentNode;
      ParentNode := treeView2CurrentNode.Parent;
      ParentNode.Collapse(False);
      ParentNode.Expand(False);
      saveNode.Collapse(False);
      saveNode.Expand(False);
      Form1.ShellTreeView2.Selected := saveNode;
    end // END CASE
    else
    begin
      treeView2CurrentNode.Collapse(False);
      treeView2CurrentNode.Expand(False);
    end // END CASE
  ; // END IF
  ; // END IF
  if (activeTree = 2) then
    if dir1Empty then
    begin
      saveNode := treeView1CurrentNode;
      ParentNode := treeView1CurrentNode.Parent;
      ParentNode.Collapse(False);
      ParentNode.Expand(False);
      saveNode.Collapse(False);
      saveNode.Expand(False);
      Form1.ShellTreeView1.Selected := saveNode;
    end // END CASE
    else
    begin
      treeView1CurrentNode.Collapse(False);
      treeView1CurrentNode.Expand(False);
    end // END CASE
  ; // END IF
  ; // END IF

end;

// COPY FILE

procedure fileCopy();
var
  buffer: TMemoryStream;
begin

  if ExtractFileName(fullPathDst) = '' then
    fullPathDst := fullPathDst + ExtractFileName(fullPathSrc);

  if InputQuery('Copy ' + fullPathSrc, 'to', False, fullPathDst) then
  begin
    buffer := TMemoryStream.Create;
    buffer.LoadFromFile(fullPathSrc);
    buffer.SaveToFile(fullPathDst);
    buffer.Free;
  end;

end;

// COPY DIRECTORY

procedure CopyDirectory(sourceDir, destDir: string);

  procedure CopyDir(sourceDir, destDir: string);
  var
    searchRec: TSearchRec;
    sourcePath, destPath: string;
  begin
    sourcePath := IncludeTrailingPathDelimiter(sourceDir);
    destPath := IncludeTrailingPathDelimiter(destDir);
    CreateDir(destPath);

    if FindFirst(sourcePath + '*', faAnyFile, searchRec) = 0 then
    begin
      repeat

        if (SearchRec.Attr and faDirectory) <> 0 then
        begin
          if (searchRec.Name = '.') or (searchRec.Name = '..') then
            Continue
          else
            CopyDir(sourcePath + searchRec.Name, destPath + searchRec.Name);
          // END IF
        end // END BEGIN
        else
        begin
          CopyFile(PChar(sourcePath + searchRec.Name),
            PChar(destPath + searchRec.Name), False);
        end // END BEGIN
        ; // END IF
      until FindNext(searchRec) <> 0;
      FindClose(searchRec);
    end // END BEGIN
    ; // END IF

  end; // END NESTED PROCEDURE

begin

  message := 'Copy Directory ' + fullPathSrc + sLineBreak + 'to' +
    sLineBreak + fullPathDst;
  case MessageDlgPos(message, mtConfirmation, [mbYes, mbCancel], 0, 200, 200) of
    mrCancel: Exit;
    mrYes: CopyDir(sourceDir, destDir);
  end; // END CASE

end;

// MOVE - Set it Up

procedure x_Move();
begin

  x_Copy;
  x_Delete;

end;

// MKDIR - Set it Up

procedure x_MkDir(Sender: TObject);
begin

  if (activeTree = 1) then
  begin
    fullPathSrc := Form1.ShellTreeView1.Path;
    dir1Empty := IsDirectoryEmpty(ExtractFilePath(Form1.ShellTreeView1.Path));
    //      Y := 1;
  end
  else
  begin
    fullPathSrc := Form1.ShellTreeView2.Path;
    dir2Empty := IsDirectoryEmpty(ExtractFilePath(Form1.ShellTreeView2.Path));
    //      Y := 2;
  end;  // ENDIF

  makeDir;

  if (activeTree = 1) then
    if dir1Empty then
    begin
      saveNode := treeView1CurrentNode;
      ParentNode := treeView1CurrentNode.Parent;
      ParentNode.Collapse(False);
      ParentNode.Expand(False);
      saveNode.Collapse(False);
      saveNode.Expand(False);
      Form1.ShellTreeView1.Selected := saveNode;
    end // END CASE
    else
    begin
      treeView1CurrentNode.Collapse(False);
      treeView1CurrentNode.Expand(False);
    end // END CASE
  ; // END IF
  ; // END IF
  if (activeTree = 2) then
    if dir2Empty then
    begin
      saveNode := treeView2CurrentNode;
      ParentNode := treeView2CurrentNode.Parent;
      ParentNode.Collapse(False);
      ParentNode.Expand(False);
      saveNode.Collapse(False);
      saveNode.Expand(False);
      Form1.ShellTreeView2.Selected := saveNode;
    end // END CASE
    else
    begin
      treeView2CurrentNode.Collapse(False);
      treeView2CurrentNode.Expand(False);
    end // END CASE
  ; // END IF
  ; // END IF

end;

// MKDIR - Do It

procedure makeDir();
begin

  str1 := '';
  if not InputQuery('make Directory', 'Enter the new directory Name', False, str1) then
    exit;

  str2 := fullPathSrc + str1;
  message := 'Create Directory ' + str2;
  case MessageDlgPos(message, mtConfirmation, [mbYes, mbCancel], 0, 200, 200) of
    mrCancel: Exit;
    mrNo: ;
    mrYes:
    begin
      CreateDir(str2);
    end; // END BEGIN
  end; // END CASE

end;

// DELETE - Set it Up

procedure x_Delete();
begin

  if activeTree = 1 then
  begin
    fullPathSrc := Form1.ShellTreeView1.Path;
    //      Y := 1;
  end // END BEGIN
  else
  begin
    fullPathSrc := Form1.ShellTreeView2.Path;
    //      Y := 2;
  end // END BEGIN
  ;  // ENDIF

  directoryFlag := False;
  if (RightStr(fullPathSrc, 1) = '/') then
  begin
    directoryFlag := True;
    dirDelete;
  end // END BEGIN
  else
    fileDelete; // END IF

  if activeTree = 1 then
  begin
    ParentNode := treeView1CurrentNode.Parent;
    ParentNode.Collapse(False);
    ParentNode.Expand(False);
  end // END BEGIN
  else
  begin
    ParentNode := treeView2CurrentNode.Parent;
    ParentNode.Collapse(False);
    ParentNode.Expand(False);
  end // END BEGIN
  ;

end;

// DELETE FILE - Do It

procedure fileDelete();
begin

  message := 'Delete File ' + fullPathSrc;
  case MessageDlgPos(message, mtConfirmation, [mbYes, mbCancel], 0, 200, 200) of
    mrCancel: Exit;
    mrYes:
    begin
      DeleteFile(fullPathSrc);
    end; // END BEGIN
  end; // END CASE

end;

// DELETE DIRECTORY - Do It

procedure dirDelete();
begin

  message := 'Delete Directory ' + fullPathSrc;
  case MessageDlgPos(message, mtConfirmation, [mbYes, mbCancel], 0, 200, 200) of
    mrCancel: Exit;
    mrYes:
    begin
      if DeleteDirectory(fullPathSrc, True) then
        RemoveDirUTF8(fullPathSrc); // END IF
    end; // END BEGIN
  end; // END CASE

end;

// IS DIRECTORY EMPTY

function IsDirectoryEmpty(Directory: string): boolean;
var
  SearchRec: TSearchRec;
begin
  X := FindFirst(IncludeTrailingPathDelimiter(Directory) + '*', faAnyFile, SearchRec);
  X := FindNext(SearchRec);
  X := FindNext(SearchRec);

  if X = -1 then
    Result := True
  else
    Result := False;

  FindClose(SearchRec);

end;

//=====================================================
//  ShellTreeView1
//=====================================================

// EXPANDED

procedure TForm1.ShellTreeView1SelectionChanged(Sender: TObject);
begin
  treeView1CurrentNode := ShellTreeView1.Selected;
end;

// CHANGED

procedure TForm1.ShellTreeView1Expanded(Sender: TObject; Node: TTreeNode);
begin
  treeView1CurrentNode := ShellTreeView1.Selected;
end;

//-----------------------------------------------------
//  Single Click
//-----------------------------------------------------
procedure TForm1.ShellTreeView1Click(Sender: TObject);
begin

  ShellTreeView1.BackgroundColor := clMenu;
  ShellTreeView2.BackgroundColor := clGradientInactiveCaption;
  activeTree := 1;

end;

//-----------------------------------------------------
//   Double Click
//-----------------------------------------------------
procedure TForm1.ShellTreeView1DblClick(Sender: TObject);
var
  key: word;
  Shift: TShiftState;
  MousePos: TPoint;
begin
  MousePos := Mouse.CursorPos;
  MousePos := ShellTreeView1.ScreenToClient(MousePos);
  saveNode := ShellTreeView1.GetNodeAt(MousePos.X, MousePos.Y);
  if saveNode = nil then
    exit;

  activeTree := 1;
  key := VK_F4;
  FormKeyUp(Sender, key, Shift);

end;

//-----------------------------------------------------
//  Popup Menu1
//-----------------------------------------------------
procedure TForm1.PopupMenu1Popup(Sender: TObject);
begin
  activeTree := 1;
  ShellTreeView1Click(Sender);
end;

procedure TForm1.PopupFavouritesClick(Sender: TObject);
var
  SubItem: TMenuItem;
begin

  if activeTree = 1 then
    str1 := ExtractFilePath(Form1.ShellTreeView1.Path)
  else
    str1 := ExtractFilePath(Form1.ShellTreeView2.Path); // END IF

  SubItem := TMenuItem.Create(Self);
  SubItem.Caption := Str1;
  SubItem.OnClick := @ProjectMenuClick;
  ProjectMenu.Add(subItem);

end;

//=====================================================
//  ShellTreeView2
//=====================================================

// EXPANDED

// CHANGED

procedure TForm1.ShellTreeView2SelectionChanged(Sender: TObject);
begin
  treeView2CurrentNode := ShellTreeView2.Selected;
end;

procedure TForm1.ShellTreeView2Expanded(Sender: TObject; Node: TTreeNode);
begin
  treeView2CurrentNode := ShellTreeView2.Selected;
end;

//-----------------------------------------------------
//   TREE 2 Single Click
//-----------------------------------------------------
procedure TForm1.ShellTreeView2Click(Sender: TObject);
begin

  ShellTreeView2.BackgroundColor := clMenu;
  ShellTreeView1.BackgroundColor := clGradientInactiveCaption;
  activeTree := 2;

end;

//-----------------------------------------------------
//   TREE 2 Double Click
//-----------------------------------------------------
procedure TForm1.ShellTreeView2DblClick(Sender: TObject);
var
  key: word;
  Shift: TShiftState;
  MousePos: TPoint;
begin
  MousePos := Mouse.CursorPos;
  MousePos := ShellTreeView2.ScreenToClient(MousePos);
  saveNode := ShellTreeView2.GetNodeAt(MousePos.X, MousePos.Y);
  if saveNode = nil then
    exit;

  activeTree := 2;
  key := VK_F4;
  FormKeyUp(Sender, key, Shift);

end;

//-----------------------------------------------------
//  TREE 2 Popup Menu2
//-----------------------------------------------------
procedure TForm1.PopupMenu2Popup(Sender: TObject);
begin
  activeTree := 2;
  ShellTreeView2Click(Sender);
end;


//=====================================================
//  BOOKMARKS
//=====================================================

// DOUBLE CLICK

procedure TForm1.BookmarkGridDblClick(Sender: TObject);
var
  MousePos: TPoint;
begin

  MousePos := Mouse.CursorPos;
  MousePos := BookmarkGrid.ScreenToClient(MousePos);
  BookmarkGrid.MouseToCell(MousePos.X, MousePos.Y, X, Y);
  if X <> 0 then exit;


  str1 := BookmarkGrid.Cells[1, Y];
  if str1 <> '' then
  begin
    FocusedSynEdit.TopLine := StrToInt(str1);
    toolsYes := True;
    Tools;
    FocusedSynEdit.SetFocus();
  end // END BEGIN
  ; // END IF

end;

//=====================================================
//  M_Execute
//=====================================================

procedure TForm1.RunExtGridCheckboxToggled(Sender: TObject;
  aCol, aRow: integer; aState: TCheckboxState);
begin
  Shortcuts;
end;

procedure Shortcuts();
var
  Ctrl: TControl;
begin
  for Y := 0 to Form1.PanelC2.ControlCount - 1 do
  begin
    Ctrl := Form1.PanelC2.Controls[Y];
    Ctrl.Visible := False;
  end // END BEGIN
  ; // END FOR

  if Form1.Edit_PageControl.ActivePageIndex < 0 then exit;


  for X := 1 to Form1.RunExtGrid.RowCount - 1 do
  begin
    Str1 := Form1.RunExtGrid.Cells[2, X];
    if (Str1 = '1') and (Form1.RunExtGrid.Cells[1, X] <> '') then
    begin
      for Y := 0 to Form1.PanelC2.ControlCount - 1 do
      begin
        Ctrl := Form1.PanelC2.Controls[Y];
        if Ctrl.Visible = False then
        begin
          // Stupid way to do this but the controls are not returned in order
          case Y of
            0: begin
              Form1.SC0.Visible := True;
              Form1.SC0.Caption := Form1.RunExtGrid.Cells[1, X];
              break;
            end;
            1: begin
              Form1.SC1.Visible := True;
              Form1.SC1.Caption := Form1.RunExtGrid.Cells[1, X];
              break;
            end;
            2: begin
              Form1.SC2.Visible := True;
              Form1.SC2.Caption := Form1.RunExtGrid.Cells[1, X];
              break;
            end;
            3: begin
              Form1.SC3.Visible := True;
              Form1.SC3.Caption := Form1.RunExtGrid.Cells[1, X];
              break;
            end;
            4: begin
              Form1.SC4.Visible := True;
              Form1.SC4.Caption := Form1.RunExtGrid.Cells[1, X];
              break;
            end;
            5: begin
              Form1.SC5.Visible := True;
              Form1.SC5.Caption := Form1.RunExtGrid.Cells[1, X];
              break;
            end;
            6: begin
              Form1.SC6.Visible := True;
              Form1.SC6.Caption := Form1.RunExtGrid.Cells[1, X];
              break;
            end;
            7: begin
              Form1.SC7.Visible := True;
              Form1.SC7.Caption := Form1.RunExtGrid.Cells[1, X];
              break;
            end;
          end; // END CASE
        end // END BEGIN
        ; // END IF
      end // END BEGIN
      ; // END FOR
    end // END BEGIN
    else
      Form1.RunExtGrid.Cells[2, X] := '0'; // END IF
  end // END BEGIN
  ; // END FOR

end;


// DOUBLE CLICK

procedure TForm1.RunExtGridDblClick(Sender: TObject);
var
  MousePos: TPoint;
begin

  MousePos := Mouse.CursorPos;
  MousePos := RunExtGrid.ScreenToClient(MousePos);
  RunExtGrid.MouseToCell(MousePos.X, MousePos.Y, X, Y);
  if X <> 0 then exit;
  if Y = 0 then exit;

  Str1 := RunExtGrid.Cells[3, Y];                // Working Directory
  Str2 := RunExtGrid.Cells[4, Y];                // Program Name
  Str3 := GetParameter(RunExtGrid.Cells[5, Y]);  // Parameters

  runExternal(Str1, Str2, Str3);

end;

//-----------------------------------------------------
//  M_Execute - Shortcut Click
//-----------------------------------------------------
procedure TForm1.ShortcutClick(Sender: TObject);
begin

  Str1 := (Sender as TButton).Caption;

  for Y := 1 to Form1.RunExtGrid.RowCount - 1 do
  begin
    Str2 := Form1.RunExtGrid.Cells[1, Y];
    if Str1 = Str2 then
    begin
      Str1 := Form1.RunExtGrid.Cells[3, Y];        // Working Directory
      Str2 := Form1.RunExtGrid.Cells[4, Y];        // Program Name
      Str3 := GetParameter(RunExtGrid.Cells[5, Y]);  // Parameters
      runExternal(Str1, Str2, Str3);
      break;
    end // END CASE
    ; // END IF
  end // END BEGIN
  ; // END FOR

end;

//-----------------------------------------------------
//  M_Execute - Extract Parameters
//-----------------------------------------------------
function GetParameter(s: string): string;
begin

  Str4 := files[Form1.Edit_PageControl.ActivePageIndex].Name;
  if LeftStr(s, 3) = '*.*' then
    s := ExtractFileName(Str4)
  else
  if s = '*.' then
  begin
    s := ExtractFileName(Str4);
    for X := length(s) downto 1 do
    begin
      if MidStr(s, X, 1) = '.' then
      begin
        s := LeftStr(s, X - 1);
        break;
      end // END BEGIN
      ; // END IF
    end // END BEGIN
    ; // END FOR
  end // END BEGIN
  ; // END IF
  ; // END IF

  Result := s;

end;

// APROCESS

procedure runExternal(dir, prog, param: string);
var
  AProcess: TProcess;
  AStringList: TStringList;
begin

  saveFile;

  Screen.BeginWaitCursor;
  Application.ProcessMessages;

  AProcess := TProcess.Create(nil);
  AProcess.CurrentDirectory := dir;
  AProcess.Executable := prog;
	if prog = 'bash' then
	  AProcess.Parameters.Add('-c');
  AProcess.Parameters.Add(param);

  AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];

  AProcess.Options := AProcess.Options + [poNoConsole];
  AProcess.ShowWindow := swoHide;

  AProcess.Execute;

  AStringList := TStringList.Create;
  AStringList.LoadFromStream(AProcess.Output);
  if AStringList.Count = 0 then
    AStringList.LoadFromStream(AProcess.Stderr);

  Form1.Output_Memo.Lines.Assign(AStringList);

  if toolsYes = False then
    Tools;
  Form1.Tools_PageControl.ActivePage := Form1.Tools_Output;

  AStringList.Free;
  AProcess.Free;
  Screen.EndWaitCursor;

end;

// APROCESS - for Terminal

procedure Terminal(Str1: string);
const
  BUF_SIZE = 2048; // Buffer size for reading the output in chunks

var
  RunExt				: array of string;
  AProcess			: TProcess;
  BytesRead			: longint;
  Buffer				: array[1..BUF_SIZE] of byte;

begin
  GetDir(0, pwd);
  RunExt := Str1.split([' ']);

  //CD is the only internal bash command I would use and
  //it's easier doing this than trying to figure out how
  //to pass parameters for Bash
  if PChar(UpperCase(RunExt[0].TrimLeft)) = 'CD' then
  begin
    Str1 := RunExt[1].TrimLeft;
    if Str1[1] <> '/' then
      Str1 := pwd + '/' + Str1;
			try
    		ChDir(Str1);
			except
    		on E: Exception do
					begin
      			MessageDlgPos('Invalid Directory', mtConfirmation, [mbCancel], 0, 1200, 600);
						exit;
					end
    	end;
  		GetDir(0, pwd);
      Form1.TerminalOut.Lines.Add(pwd);
			exit;
    ; // ENDIF
  end // END BEGIN
  ; // END IF

	//CLEAR
  if PChar(UpperCase(RunExt[0].TrimLeft)) = 'CLEAR' then
  	begin
      Form1.TerminalOut.Lines.Clear;
      Form1.TerminalOut.Lines.Add(pwd);
			exit;
	  end // END BEGIN
  ; // END IF

  AProcess := TProcess.Create(nil);
  AProcess.CurrentDirectory := pwd;
  AProcess.Executable := PChar(RunExt[0].TrimLeft);

  for X := 1 to High(RunExt) do
  begin
    Str1 := PChar(RunExt[X].TrimLeft);
    AProcess.Parameters.Add(Str1);
  end // END BEGIN
  ; // END OF

  AProcess.Options := AProcess.Options + [poUsePipes,ponoconsole];

  try
  	AProcess.Execute;
  except
    on E: Exception do
    	MessageDlgPos('Executable not found', mtConfirmation, [mbCancel], 0, 1200, 600);
  end;

	Str1 := ' ';
  repeat
  	BytesRead := AProcess.Output.Read(Buffer, BUF_SIZE);
		If BytesRead <> 0 then
			begin
				J := 0;
				repeat
					inc (J);
					if Buffer[J] = $0A then
						begin
							Form1.TerminalOut.Lines.Add(Str1);
							Str1 := ' ';
						end // END BEGIN
					else // ELSE
						Str1 := Str1 + char(Buffer[J]);
					; // END IF
				until J = BytesRead;
			end // END BEGIB
		; // END IF
  until BytesRead = 0;

  Form1.TerminalOut.Lines.Add(pwd);

	Form1.TerminalOut.selstart := MaxInt;

  AProcess.Free;

end;


//-----------------------------------------------------
// END PROGRAM
//-----------------------------------------------------
end.
