Here is the MMenu_UpdateItem function.
First the example script:
Code:
#SingleInstance force
#NoEnv
main := MMenu_Create()
prog := MMenu_Create()
wind := MMenu_Create()
regi := MMenu_Create()
MMenu_Add( main, "Move on a item","", "", "d iDoNothing")
MMenu_Add( main, "Programs", "", "", "m" prog)
MMenu_Add( main, "Windows", "", "", "m" wind)
MMenu_Add( main, "Registry", "", "", "m" regi)
MMenu_Add(prog, "Please wait!", "", "", "d iPInfo")
MMenu_Add(prog, "Path", "", "", "iDoNothing")
MMenu_Add(prog, "# of folders", "", "", "iDoNothing")
MMenu_Add(prog, "# of files", "", "", "iDoNothing")
MMenu_Add(prog, "Last modified", "", "", "iDoNothing")
MMenu_Add(prog, "Sum file size", "", "", "iDoNothing")
MMenu_Add(prog, "Result", "", "", "b| d iPDoNothing")
MMenu_Add(prog, "x", "", "", "iPPath")
MMenu_Add(prog, "x", "", "", "iPFolder")
MMenu_Add(prog, "x", "", "", "iPFiles")
MMenu_Add(prog, "x", "", "", "iPLastMod")
MMenu_Add(prog, "x", "", "", "iPSize")
MMenu_Add(wind, "Please wait!", "", "", "d iWInfo")
MMenu_Add(wind, "Path", "", "", "iDoNothing")
MMenu_Add(wind, "# of folders", "", "", "iDoNothing")
MMenu_Add(wind, "# of files", "", "", "iDoNothing")
MMenu_Add(wind, "Last modified", "", "", "iDoNothing")
MMenu_Add(wind, "Sum file size", "", "", "iDoNothing")
MMenu_Add(wind, "Result", "", "", "b| d iDoNothing")
MMenu_Add(wind, "x", "", "", "iWPath")
MMenu_Add(wind, "x", "", "", "iWFolder")
MMenu_Add(wind, "x", "", "", "iWFiles")
MMenu_Add(wind, "x", "", "", "iWLastMod")
MMenu_Add(wind, "x", "", "", "iWSize")
MMenu_Add(regi, "Please wait!", "", "", "d iRInfo")
MMenu_Add(regi, "Path", "", "", "iDoNothing")
MMenu_Add(regi, "# of keys", "", "", "iDoNothing")
MMenu_Add(regi, "# of values", "", "", "iDoNothing")
MMenu_Add(regi, "Last modified", "", "", "iDoNothing")
MMenu_Add(regi, "Result", "", "", "b| d iDoNothing")
MMenu_Add(regi, "x", "", "", "iRPath")
MMenu_Add(regi, "x", "", "", "iRKeys")
MMenu_Add(regi, "x", "", "", "iRValues")
MMenu_Add(regi, "x", "", "", "iRLastMod")
PathsToBeSearched = ;collect the root paths
(LTrim
Folder|%A_ProgramFiles%|%prog%|P
Folder|%A_WINDIR%|%wind%|W
Registry|HKCU\Software\Microsoft\Windows NT\CurrentVersion|%regi%|R
)
MMenu_UpdateItems(PathsToBeSearched) ;start to search the root paths and update the menus
;show menu
MMenu_Show( main, A_SCreenWidth/2-100, A_ScreenHeight/2-100, "OnMMenuSelection", "UOnUInit")
ExitApp
return
#include includes
#include MMenu_UpdateItems.ahk
#include MMenu.ahk
#include structs.ahk
OnUInit:
If (MMenu = main) ;when main menu closes, stop search (if not finished yet).
MMenu_UpdateItems("Stop")
return
OnMMenuSelection:
return
And this is the function that can be included (after the autoexec section)
Code:
Script = MMenu_UpdateItems
Version = 1.0
msgbox, 16, Wrongly included, This script "%Script%" is wrongly included.`n Since it contains a subroutine with a return, it should be included after the auto-exec section.`nIf done correctly this Msgbox will not pop up.
Return
; update menu items with info on paths
; item menu names must have a common name (ID) and end with "Info/Path/Folder/Keys/Files/Values/LastMod/Size"
; to start the gathering of data specify a string with the data to collect
; to stop the gathering, specify "S[top]" as the parameter
;
; the string with the data to collect is a list of root folders or keys for which data should be put into menu
; needed data for each root folder/key are: "type | path | menu # | menu item names"
; type either F[older] or R[egistry]
; path the root path (folder or key) from which the search starts
; menu # menu number the fields are in
; menu item names the common beginning of the menu item IDs
; - their individual ID ends with:
; Info
; Path
; Folder Or Keys
; Files Or Values
; LastMod
; Size (not available for registry paths)
; Nothing happens in the case that idividual items do not exist in the menu. Thus you can leave some fields out.
; you need to have permission to read the folders or keys to get the data
; as a limited user you might not be able to get the last modified date for registry keys even though that you can read them
MMenu_UpdateItems(String){
global MMenu_UpdateItems_Stop
If (InStr(String,"s")=1) { ;check if the search should be stopped
MMenu_UpdateItems_Stop := True ;set status var
Return
}
MMenu_UpdateItems_Stop := False ;set status var
;set the list of root folders for which data should be put into menu
MMenu_UpdateItems_NextFolder(String)
;start extra thread to gather data for root folders and fill it into menu while it is shown
SetTimer, MMenu_UpdateItems, On
}
;get the number of folders and files/keys and values for a given path and put into menu
MMenu_UpdateItems:
SetTimer, MMenu_UpdateItems, Off ;stop timer, it will restart itself when needed
If MMenu_UpdateItems_Stop ;stop depending on status var, e.g. when menu closed
Return
If ! MMenu_UpdateItems { ;if there is currently no root folder that needs to be looked at
If ! MMenu_UpdateItems := MMenu_UpdateItems_NextFolder() ;get next root folder with data
Return ;when there is no root folder left stop the gathering of data
StringSplit, MMenu_UpdateItems, MMenu_UpdateItems, | ;split root folder/key data into components
MMenu_UpdateItems_Type := MMenu_UpdateItems1 ;root path type
MMenu_UpdateItems := MMenu_UpdateItems2 ;root path
MMenu_UpdateItems_Menu := MMenu_UpdateItems3 ;menu number
MMenu_UpdateItems_Name := MMenu_UpdateItems4 ;menu item names
;fill menu with root path
MMenu_Set(MMenu_UpdateItems_Menu, MMenu_UpdateItems_Name "Path", MMenu_UpdateItems)
;inform user by changing the menu title
MMenu_Set(MMenu_UpdateItems_Menu, MMenu_UpdateItems_Name "Info", "Please wait!")
; FileAppend, %MMenu_UpdateItems%`n, Filename.txt
;reset data and gather from root path
If (InStr(MMenu_UpdateItems_Type,"f")=1)
MMenu_UpdateItems := MMenu_UpdateItems_Folder(MMenu_UpdateItems "`n",MMenu_UpdateItems_Menu,MMenu_UpdateItems_Name,"Reset")
Else
MMenu_UpdateItems := MMenu_UpdateItems_Registry(MMenu_UpdateItems "`n",MMenu_UpdateItems_Menu,MMenu_UpdateItems_Name,"Reset")
}Else
;gather data for folders in paths
If (InStr(MMenu_UpdateItems_Type,"f")=1)
MMenu_UpdateItems := MMenu_UpdateItems_Folder(MMenu_UpdateItems,MMenu_UpdateItems_Menu,MMenu_UpdateItems_Name)
Else
MMenu_UpdateItems := MMenu_UpdateItems_Registry(MMenu_UpdateItems,MMenu_UpdateItems_Menu,MMenu_UpdateItems_Name)
; FileAppend, %MMenu_UpdateItems%`n, Filename.txt
If MMenu_UpdateItems ;if there are still folders to be looked at
SetTimer, MMenu_UpdateItems, 250 ;start itself in an extra thread to look for these folders
Else{ ;otherwise
MMenu_Set(MMenu_UpdateItems_Menu, MMenu_UpdateItems_Name "Info", "Details:") ;inform user by changing the menu title
SetTimer, MMenu_UpdateItems, On ;start itself again for next root folder
}
Return
;get the number of folders and files for a given path
; it stops working after a while to let a menu update
; optimized for speed and responsiveness of the menu
MMenu_UpdateItems_Folder(FolderList,menu,name,Reset = 0){
Static NumFolder,NumFiles,Size,LastModTime,LastFolder
SetBatchLines, -1 ;speed!
If Reset { ;reset, e.g. a new folder path gets searched
LastModTime = 0
NumFolder = 0
NumFiles = 0
Size = 0
LastFolder =
}
Loop, Parse, FolderList, `n ;go through list of folders
{
If (A_Index > 75) ;after 75 folders got searched, stop (will be resumed from here with next call)
Break
If A_LoopField is space ;don't search pure `n
Continue
DoneFolder .= A_LoopField "`n" ;remember folders that have been searched
If (LastFolder = A_LoopField) ;never scan a folder twice (can happen when there was an error during reading the first time)
Continue
LastFolder = %A_LoopField%
Loop, %A_LoopField%\*, 1, 0 ;get subfolders and files in that folder
{
If InStr(A_LoopFileAttrib, "D") { ;count folders and remember their subfolders to be searched
NumFolder++
FolderList .= A_LoopFileLongPath "`n"
}Else{ ;count files, add file size and get latest mod time
NumFiles++
Size += A_LoopFileSize
}
LastModTime := LastModTime > A_LoopFileTimeModified ? LastModTime : A_LoopFileTimeModified
}
}
StringReplace, FolderList, FolderList, %DoneFolder% ;remove already searched folders from list
MMenu_Set(menu, name "Folder", NumFolder) ;set until-now gathered data into menu fields
MMenu_Set(menu, name "Files", NumFiles)
MMenu_Set(menu, name "Size", MMenu_UpdateItems_HumanReadableSize(Size))
MMenu_Set(menu, name "LastMod", MMenu_UpdateItems_HumanReadableDate(LastModTime))
Return FolderList ;return still to be searched list of folders
}
MMenu_UpdateItems_Registry(KeyList,menu,name,Reset = 0){
Static NumKeys,NumValues,LastModTime,Lastkey
SetBatchLines, -1 ;speed!
If Reset { ;reset, e.g. a new registry path gets searched
LastModTime = 0
NumKeys = 0
NumValues = 0
Lastkey =
}
Loop, Parse, KeyList, `n ;go through list of keys
{
If (A_Index > 75) ;after 75 keys got searched, stop (will be resumed from here with next call)
Break
If A_LoopField is space ;don't search pure `n
Continue
DoneKeys .= A_LoopField "`n" ;remember keys that have been searched
If (Lastkey = A_LoopField) ;never scan a key twice (can happen when there was an error during reading the first time)
Continue
Lastkey = %A_LoopField%
StringLeft, RootKey, A_LoopField, InStr(A_LoopField,"\") - 1 ;get root key and key
StringTrimLeft, Key, A_LoopField, InStr(A_LoopField,"\")
Loop, %RootKey%, %Key%, 1, 0 ;get subkeys and values in that key
{
If InStr(A_LoopRegType, "Key") { ;count subkeys and remember their full key path to be searched
NumKeys++
KeyList .= A_LoopRegKey "\" A_LoopRegSubKey "\" A_LoopRegName "`n"
LastModTime := LastModTime > A_LoopRegTimeModified ? LastModTime : A_LoopRegTimeModified
}Else{ ;count values and get latest mod time
NumValues++
}
}
}
StringReplace, KeyList, KeyList, %DoneKeys% ;remove already searched keys from list
MMenu_Set(menu, name "Keys", NumKeys) ;set until-now gathered data into menu fields
MMenu_Set(menu, name "Values", NumValues)
MMenu_Set(menu, name "LastMod", MMenu_UpdateItems_HumanReadableDate(LastModTime))
Return KeyList ;return still to be searched list of keys
}
;convert file size in bytes into Gb/Mb/Kb and concatenate unit
MMenu_UpdateItems_HumanReadableSize(Size){
If (Size > 1024 * 1024 * 1024)
Size := Round(Size / (1024 * 1024 * 1024),2) " Gb"
Else If (Size > 1024 * 1024)
Size := Round(Size / (1024 * 1024),2) " Mb"
Else If (Size > 1024)
Size := Round(Size / 1024,2) " Kb"
Else
Size .= " b"
Return Size
}
;convert a date string into a shortdate
MMenu_UpdateItems_HumanReadableDate(Date){
FormatTime, Date, %Date%, ShortDate
Return Date
}
;Return the next topmost item in a `n separated list and remove it from the list
;specify SetAsNewList to set a new list
MMenu_UpdateItems_NextFolder(SetAsNewList = ""){
Static List
If SetAsNewList {
List = %SetAsNewList%
Return
}
StringSplit, Item, List, `n
List := RegExReplace(List,"\Q" Item1 "\E\n?", "", "", 1, 1)
Return Item1
}