#SingleInstance force
#Include Anchor.ahk
#Include EnvVars.ahk

#NoEnv

SetBatchLines -1
ScanStatus := False

Version = 0.2
ScriptName = VP Script Editor v%Version%
SelectedIcn = 0



/*
Detect if the buton is launched with script name
*/

EditOnly=
fnode = %1%
if ( fnode = "/edit"){
  EditOnly = true
  fnode = %2%
}else if ( fnode = "/ini" ) {
  IniFile := EnvVars("%APPDATA%\VPScriptEdit.ini")
  IniRead, SCEditor,  %IniFile%, Settings, editor, notepad.exe
  Run , %SCEditor% "%IniFile%"
  return 
}

if ( FileExist( fnode ) ){
  FileReadLine, xx, %fnode%,1
  RegExMatch(xx, "\\\\\\([^\\]+)\\[^>]*(>.+)?", fnode)
  VPRoot := fnode1
  node := fnode2
} else {
  return
}


/*
Get virtual path path when script is launched
*/

EnvGet , CurrentVPP , ${%VPRoot%}Path

fchar := SubStr(node, 1,1)

if ( fchar != ">"  ){

  MsgBox , 292, Warning, Script must start with '>' char `nDo you want to create script ?
  IfMsgBox Yes
    InputBox ,node, New Node, Enter node name without '>' !
  else
    return
  node = >%node%
}

ScriptName .= "    : " . CurrentVPP . node
File  = %A_TEMP%\%A_sec%%A_MM%%A_Hour%%A_MSec%.scvp




If ( A_OSType = "WIN32_WINDOWS" )  ; Windows 9x
    DefaultItem = %A_WinDir%\system\shell32.dll
Else
    DefaultItem = %A_WinDir%\system32\shell32.dll

;tray menu
Menu, Tray, Icon, %DefaultItem%, 81   ;icon for taskbar and for process in task manager
Menu, Tray, Tip, %ScriptName%
Menu, Tray, NoStandard
Menu, Tray, Add, Exit, GuiClose
Menu, Tray, Default, Exit
Menu, Tray, Click, 1

;get ini file name and values
SplitPath, A_ScriptName, , , , OutNameNoExt
IniFile := EnvVars("%APPDATA%\VPScriptEdit.ini")

IniRead, RadFile,   %IniFile%, Settings, RadFile,   0
IniRead, RadReport, %IniFile%, Settings, RadReport, 0
IniRead, RadTile,   %IniFile%, Settings, RadTile,   1
IniRead, RadIcons,  %IniFile%, Settings, RadIcons,  0
IniRead, RadSmallIcons, %IniFile%, Settings, RadSmallIcons, 0
IniRead, RadList,       %IniFile%, Settings, RadList,       0
IniRead, SCEditor,       %IniFile%, Settings, editor, notepad.exe


Gui, 1:+Resize
Gui, 1:Add, GroupBox,xm w120 r1.2 Section ,Select
Gui, 1:Add, Radio,xp+10 yp+20 Section Checked vRadFile, File
Gui, 1:Add, Radio,ys , Folder
If RadFile = 2
    GuiControl, 1:, Folder, 1
Gui, 1:Add, Edit, ym+17 w250 r1  vScanItem, %ScanItem%
Gui, 1:Add, Button, ym+16 vBtnBrowse gBtnBrowse,&...
Gui, 1:Add, Button, ym+16 vBtnClearIcn gBtnClearIcn ,&No Icon
Gui, 1:Add, Button, ym+16 w50 vBtnScan gBtnScan Default, s&Can
Gui, 1:Add, ListView, xm r2 w540 -Multi AltSubmit Icons vLstOfIcons gLstOfIcons Section,Icon&Nr|File name|Internal ID
Gui, 1:Add, Text, w160 vTxtIconsFound Section, 0000 icons found, shown as
Gui, 1:Add, Radio, ys Checked%RadReport% gRadReport vRadReport ,&Report
Gui, 1:Add, Radio, ys Checked%RadTile% gRadTile vRadTile , &Tile
Gui, 1:Add, Radio, ys Checked%RadIcons% gRadIcons vRadIcons , &Icons
Gui, 1:Add, Radio, ys Checked%RadSmallIcons% gRadSmallIcons vRadSmallIcons ,s&mall Icons
Gui, 1:Add, Radio, ys Checked%RadList% gRadList vRadList , &List

Gui, 1:Add, Edit, xm w540 r1 ReadOnly vVPScrCmd Section,
Gui, 1:Add, Button, xm w125 vReScanScript gReScanScript Section,  Reload from &VP
Gui, 1:Add, Text, ys w20 vSep,
Gui, 1:Add, Button, ys vBtnEditScr gBtnEditScr ,&Edit Script
Gui, 1:Add, Button, ys vBtnRefresh gBtnRefresh Disabled ,Load &Script
Gui, 1:Add, Button, ys vBtnUpdate gBtnUpdate ,&Update Script/Icon and Exit
Gui, 1:Add, Button, ys vBtnClose gBtnClose ,E&xit

Gui, 2:-Resize +ToolWindow +AlwaysOnTop
Gui, 2:Add, Button,  vBtnSimpleEdit gBtnUpdate, Update &Script
Gui, 2:Add, Button, ys vBtn2Close gBtnClose ,E&xit

if ( not EditOnly ) {
   Gui, 1:Show, %Gui1Pos% , %ScriptName%
   Gui, 1: +LastFound
   if ( RadReport = 1 ){
       lvmode  = +Report
    }else if (RadList = 1 ) {
      lvmode = +List
    }else if (RadTile = 1 ) {
      lvMode = +Tile
    }else if (RadIcons = 1 ) {
      lvMode = +Icon
    }else if (RadSmallIcons = 1 ) {
      lvMode = +IconSmall
    }

} else {

   Gui, 2:Show, x5 y5 , %CurrentVPP%%node%
}
Gui1UniqueID := WinExist()
;restore old size
;WinMove, ahk_id %Gui1UniqueID%, , , , %Gui1W%, %Gui1H%


gosub, doLoad
Return

;#############   End of autoexec section   ####################################
doLoad:
  GuiControl, 1:+Disabled, BtnRefresh
if ( A_IsUnicode ){
    StringToSend=<silent<export /f "%File%" "%CurrentVPP%" "%node%"
}else{
    StringToSend=<silent<export /af "%File%" "%CurrentVPP%" "%node%"
}
Send_VPCMD(StringToSend, VPRoot, CurrentVPP)
FileReadLine, VPScript, %File% , 1
tmpIco =
IcoInfo1 =
IcoInfo2 = 0
FoundPos := RegExMatch(VPScript, "<<([^<>]+)$", tmpIco)
if ( FoundPos > 0 ) {
  RegExMatch(tmpIco, "([^<>]+)\s*,\s*(\d+)\s*$", IcoInfo)
  If ( IcoInfo = "" ) {
    IcoInfo1:=tmpIco1
    IcoInfo2 = 0
  }
}

if ( tmpIco != ""  ) {
  VPScript :=  SubStr( VPScript, 1, FoundPos - 1 )
}

myScript := doVPIndent( VPScript ) . "`n"
FileDelete , %File%
if (A_IsUnicode) {
   FileAppend, %myScript%, %File%, UTF-16
} else {
   FileAppend, %myScript%, %File%
}
if ( IcoInfo1 != "" and not EditOnly ){
 	SelectedIcn := IcoInfo2
    GuiControl, 1:,ScanItem, %IcoInfo1%
    Gosub, BtnScan
}
if ( EditOnly ) {
    Gosub, BtnEditScr
}

Return


;#############   Browse to the folder from where the serach should start   ####
BtnBrowse:
  Gui, 1:+OwnDialogs
  Gui, 1:Submit, NoHide
  FileName := EnvVars( ScanItem )
  SplitPath, FileName, , StartDir
  If RadFile = 1
    {
      FileSelectFile, SelectedFile, S3, %StartDir%, Select file to start searching for icons, Files with icons (*.exe; *.dll; *.icl; *.ico; *.ani; *.cpl)
      If SelectedFile
        {
          GuiControl, 1:,ScanItem, %SelectedFile%
          Gosub, BtnScan
        }
    }
  Else
    {
      FileSelectFolder, SelectedDir, *%StartDir%, 2, Select folder to start searching for icons
      If SelectedDir
        {
          ;remove "\" if folder is a root drive, e.g. "C:\"
          StringRight, LastChar, SelectedDir, 1
          If LastChar = \
              StringTrimRight, SelectedDir, SelectedDir, 1

          GuiControl, 1:,ScanItem, %SelectedDir%
          Gosub, BtnScan
        }
    }
Return

;#############   Start or stop scanning for files   ###########################
BtnScan:
  tmpIco =
  If ScanStatus
      ;break scan loop
      ScanStatus := False
  Else
      ;start a new thread for scaning
      SetTimer, Scan, 20
return

;#############   Scan the folder for files with icons   #######################
Scan:
  SetTimer, Scan, Off

  ScanStatus := True

  Gui, 1:Submit, NoHide

  ;change lable and make default button: scan/stop
  GuiControl, 1:, BtnScan, &STOP
  GuiControl, 1:+Default, BtnScan

  ;disable redraw for speed
  GuiControl, 1:-Redraw, LstOfIcons

  ;clear list of files
  LV_Delete()

  ;create new list of small images and replace and delete old one
  IListIDSmall := IL_Create(100, 100, False)
  ListID := LV_SetImageList(IListIDSmall)
  If ListID
      IL_Destroy(ListID)

  ;create new list of large images and replace and delete old one
  IListIDLarge := IL_Create(100, 100, True)
  ListID := LV_SetImageList(IListIDLarge)
  If ListID
      IL_Destroy(ListID)
  FileName :=  EnvVars(ScanItem)
  FileGetAttrib, Attrib , %FileName%


  i = 0
  If Attrib contains D
    	GoSub, SearchFolder
  Else
    {
    	SearchFile = %ScanItem%
    	GoSub, SearchFile
    }

  ;enable redraw for speed
  GuiControl, 1:+Redraw, LstOfIcons

  ScanStatus := False

  ;rename button to scan again
  GuiControl, 1:, BtnScan, &Scan

  ;clear edit field
   GuiControl,1:,VPScrCmd, %VPScript%
   tmpIco := ""
  ;
    Gui, 1:ListView, LstOfIcons
    LV_Modify(SelectedIcn + 1, "+Select +Focus")
    GuiControl, 1:%lvmode%, LstOfIcons
    if ( lvmode = "+Report" ){

          LV_ModifyCol(1,"AutoHdr")
          LV_ModifyCol(2,"AutoHdr")
          LV_ModifyCol(3,"AutoHdr")
     }
return

SearchFile:
  ;Search for 9999 icons in the selected file
  Loop, 9999
    {
      ;get small icon
      ID := IL_Add(IListIDSmall, SearchFile , A_Index)

      ;if small icon exist
      If ID > 0
        {
          i++

          ;get large icon
          IL_Add(IListIDLarge, SearchFile , A_Index)

          ;add icon to list
          LV_Add("Icon" . A_Index, A_Index - 1, SearchFile , i)

          GuiControl,1:,TxtIconsFound,%i% icons found, shown as
        }
      Else
          Break
      ;break loop if user pushes STOP button
      If not ScanStatus
          break
    }
return

SearchFolder:
  ;create phantom list of small images and replace and delete old one
  PhantomImageListID := IL_Create(100, 100, False)
  FolderName := EnvVars( ScanItem )
  Loop, %FolderName%\*, 0, 0
    {
      ;get first icon (small)
      ID := IL_Add(PhantomImageListID, A_LoopFileFullPath, 1)

      ;If it has a first Icon ...
      If ID > 0
        {
          SearchFile = %ScanItem%\%A_LoopFileName%
          GoSub, SearchFile
        }
      ;break loop if user pushes STOP button
      If not ScanStatus
          break
    }
Return

;#############   A icon got selected in list   ################################
LstOfIcons:
  ;icon is selected with mouse or by keyboard
  If ( A_GuiEvent = "I" )
      GoSub, RadVPScrCmd
return

;#############   AHK command got changed   ####################################
RadVPScrCmd:
  ;set list of icons as default
  Gui, 1:ListView, LstOfIcons

  ;get selected
  RowNumber := LV_GetNext()

  ;If something is selected (might not be true if RadVPScrCmd is calling)
  If RowNumber
    {
      ;get Icon number
      LV_GetText(IV_IconID, RowNumber, 1)
      IV_IconID := IV_IconID
      ;get Icon File
      LV_GetText(IV_File, RowNumber, 2)

      ;get VPScrCmd
      Gui, 1:Submit, NoHide

      ;fill edit box with AHK command
      if ( IV_IconID > 0 ){
         GuiControl,1:,VPScrCmd, %VPScript%<<%IV_File%,%IV_IconID%
         tmpIco = <<%IV_File%,%IV_IconID%
      } else {
         GuiControl,1:,VPScrCmd, %VPScript%<<%IV_File%
         tmpIco = <<%IV_File%
      }

      ;new default button: copy to clipboard
      GuiControl, 1:+Default, BtnEditScr
    } else {
         GuiControl,1:,VPScrCmd, %VPScript%
         tmpIco := ""
    }
Return

;#############   Change of view mode for icon list   ##########################


RadReport:
lvMode = +Report
  GuiControl, 1:+Report, LstOfIcons
  Gui, 1:ListView, LstOfIcons
  ;Adjust width
  LV_ModifyCol(1,"AutoHdr")
  LV_ModifyCol(2,"AutoHdr")
  LV_ModifyCol(3,"AutoHdr")
Return

RadTile:
  lvMode = +Tile
  GuiControl, 1:+Tile, LstOfIcons
Return

RadIcons:
  lvMode = +Icon
  GuiControl, 1:+Icon, LstOfIcons
Return

RadSmallIcons:
  lvMode = +IconSmall
  GuiControl, 1:+IconSmall, LstOfIcons
Return

RadList:
  lvmode = +List
  GuiControl, 1:+List, LstOfIcons
Return

;#############   Copy VPScrCmd from edit box to clipboard   #################
BtnEditScr:
  Gui, 1:Submit, NoHide
  Run , %SCEditor% "%File%"
  GuiControl, 1:-Disabled, BtnRefresh
  Return

;#############   Resize window   ##############################################
GuiSize:
  ;       ControlName     , xwyh with factors [, True for MoveDraw]
 Anchor("ScanItem"        , "w")
 Anchor("BtnBrowse"       , "x", True)
 Anchor("BtnClearIcn"     , "x", True)
 Anchor("BtnScan"         , "x", True)
 Anchor("LstOfIcons"      , "wh")
 Anchor("TxtIconsFound"   , "y", True)
 Anchor("RadReport"       , "y", True)
 Anchor("RadTile"         , "y", True)
 Anchor("RadIcons"        , "y", True)
 Anchor("RadSmallIcons"   , "y", True)
 Anchor("RadList"         , "y", True)
 Anchor("Button13"        , "y", True)
 Anchor("VPScrCmd"        , "yw")
 Anchor("ReScanScript"    , "y" , True)
 Anchor("Sep"    , "y" , True)
 Anchor("BtnEditScr"      , "y", True)
 Anchor("BtnRefresh"      , "y", True)
 Anchor("BtnUpdate"       , "y", True)
 Anchor("BtnClose"        , "y", True)
Return
;############## Refresh Load Script ##########################################
ReScanScript:
gosub , doLoad
return
;############## Refresh Load Script ##########################################
BtnRefresh:
VPScript=
Loop, read , %File%
{
    VPScript .= sTrim(A_LoopReadLine)
 }
GuiControl,1:,VPScrCmd, %VPScript% %tmpIco%
return

;#############  Remove Icon     ##############################################
BtnClearIcn:
  GuiControl,1:,ScanItem, <NO ICON>
  LV_Delete()
  GuiControl,1:,VPScrCmd, %VPScript%
  tmpIco := ""
return
;####### ######   Close window   ###############################################
BtnUpdate:
myScript=
Loop, read , %File%
{
    myScript .= sTrim(A_LoopReadLine)
}

StringToSend=<silent<add /f "%CurrentVPP%\%node%"  {%myScript%%tmpIco%} <flush
Send_VPCMD(StringToSend, VPRoot, CurrentVPP)

BtnClose:
GuiClose:
GuiEscape:
  WinGetPos, PosX, PosY, SizeW, SizeH, ahk_id %Gui1UniqueID%
  Gui, 1:Submit
  IniWrite, %RadFile%,  %IniFile%, Settings, RadFile
  IniWrite, %RadReport%, %IniFile%, Settings, RadReport
  IniWrite, %RadTile%,   %IniFile%, Settings, RadTile
  IniWrite, %RadIcons%,  %IniFile%, Settings, RadIcons
  IniWrite, %RadSmallIcons%, %IniFile%, Settings, RadSmallIcons
  IniWrite, %RadList%,       %IniFile%, Settings, RadList

  FileDelete , %File%

  ExitApp
Return

; #################################################################################
Send_VPCmd(cmd , VPPath = "", VPRoot = ""){
   SentCmd :=  cmd . "-" . VPRoot . "`r" .VPPath
   VarSetCapacity( CopyDataStruct, 12 )

  if ( A_IsUnicode ){
    NumPut( 0x500056, CopyDataStruct , 0)
    NumPut( (StrLen( SentCmd )+1) * 2 , CopyDataStruct, 4 )
    NumPut( 0, SentCmd, StrLen( cmd ) *2  , "Short")
  } else {
    NumPut( 0x5056, CopyDataStruct , 0)
    NumPut( StrLen( SentCmd )+1 , CopyDataStruct, 4 )
    NumPut( 0, SentCmd, StrLen( cmd ) , "Char")
  }

  NumPut( &SentCmd, CopyDataStruct, 8 )

  SendMessage, 0x4A, , &CopyDataStruct, , ahk_class TTOTAL_CMD

return ErrorLevel
}


doVPIndent( cmd ){
  indent = 0
  output =
  state = 0
  oldstate = 0
  line  := sTrim(cmd)
  loop,  parse, line
  {
    c  := A_LoopField
     if  ( c = """" ){
      if  ( state < 0 ) {
          state := oldstate
      } else {
         oldstate := state
         state = -1
      }
    } else if ( c = "<" ){
       if  ( state = 0 ){
         state = 1
       }else if ( state > 0 ){
         output .= "`r`n"
         loop ,%indent% {
            output .= "`t"
         }
       }
     }else if ( c = "}" and state >= 0) {
       indent -= 1
         output .= "`r`n"
         loop ,%indent% {
            output .= "`t"
         }
     } else if ( c = "{" and state >= 0) {
         indent += 1
     }

      output .= c

      ;strdbg = %strdbg% / %c% %state%

   }
   return output
}

sTrim(s) {
     return s, RegExMatch(s, "s)\S.*(?<=\S)(?=\s*$)", s)
}

;# ############   End of File   ################################################
