Recent Items Filter and Browser
What It Does
This program addresses the question: ‘How to start working again with a file that you were using fairly recently, but without interrupting your present train of thought’? Anything fiddly or slow risks evaporation of the ‘brain stack’ that you’ve spent time building, so the solution has to be quick and intuitive.
The Recent Documents feature in Windows is a start. It’s a collection of links to recently used files, but if you work with lots of files it’s not the answer because it lacks filtering or a decent interface. I tried RecentFilesView from NirSoft and ActualDoc from Flexigen, but the first lacks any filtering or convenience features, and the second seemed buggy and over-featured.
In the end I wrote this script and it suits my purpose very well. Compared with search programs like Everything or Copernic, which are great for finding non-recent files, I think it’s the instant response, easy filtering, and not having to type or even think about a search term that make the difference.
It would be nice to know if anyone else finds it useful too. Also I know it works fine in W2k, XP, and Vista but I’m curious to know if it works in Win7 (I think it should).
Main Features
• A very quick and intuitive way to access recently-used files and their folders.
• A hotkey, or a click on the Tray icon, triggers an instant Explorer-style display.
• Include or reject the file types you use most simply by checking boxes.
• Click column headers to specify sorting by date or file type plus date.
• Date filter rejects files accessed more than 1 to 98 weeks ago (set by user).
• The context menu accesses the actual file not just the link.
Other Features
• Special date filter setting ‘99’ acts as infinity (i.e., no time limit applied).
• Special date filter setting ‘0’ shows the past 48 hours and disables the other filters.
• Generates check boxes itself based on the file-types you actually use: the ones in ‘Recent Documents’.
• Easy access from the Tray icon to change the Hotkey.
• User instructions via right-click on the Tray icon.
• Hotkey reminder on Status Bar.
The script contains user instructions and you can access them by right clicking on the tray icon when it's running. Or you can read them now from the script itself. Look for the label "DefineHelpMessages:" near the end.
The screen shot below shows how it looks when set to include file types doc, ahk, pdf, and txt and to cover a span of the past 3 weeks.
The program decides what check boxes to include based on a frequency count of all the file types in Recent Items. To qualify for a check box, a file type must appear at least three times. Of the ones that qualified in this case doc was the most frequent and txt the least. To see all the unchecked files, all I had to do was use Reject instead of Include, with the result below.
To see the lonely files that don't qualify for a check box, check all the boxes (using the 'Tick All' button for convenience) and click Reject.
Finally, to see all the files in Recent Documents, use 'Clr Tick' to uncheck everything, select Include, and enter 99 into 'Weeks'.
This shows how it looks with the date filter set to 48 hour mode.
This is the same except I attempted to open a non-existent file.
This is the same but shows the context menu, and
here I clicked on the column header Type to sort on both data and type.
You can download the script and the compiled version in a zip file
here or view the script
here.
If AutoHotkey is installed on the computer you can use the ahk file. Otherwise use the exe file. Either way, put the file into its own folder and click on the file to run it. When it first runs it will put an ini file into the same folder and the window should appear, together with a new icon in the system tray. Press Alt and type x, or click on the icon to toggle the window on or off. It makes no other changes to the computer so to get rid of it just delete the script and the ini file. (Except I hope you'll find it too useful to delete

)
Finally here's the script.
Code:
;;;;;;;;;;;
; ShowRecentItems_DAT.ahk
; Version 1.0
; By David Tong, 19-08-2010
;
; This program is an interface to the 'Recent Documents' folder in Windows.
; It provides a quick way to return to a recent document and features fast
; response and easy filtering to show only desired file types.
; Please refer to DefineHelpMessages: below for more detailed information.
; Dependency:
; The Find feature requires that Everything.exe by VoidTools is installed.
; Grateful acknowledgements:
; 1. The program uses some of the ini functions published by Tuncay at http://www.autohotkey.com/forum/viewtopic.php?t=46226.
; 2. The method of detecting a click on the Tray icon is by Serenity at http://www.autohotkey.com/forum/topic36960.html
; 3. The function GetMonitorAt() is from WindowPad by Laszlo.
#NoEnv
#SingleInstance , Force
SetWorkingDir %A_ScriptDir%
SetBatchLines -1
SendMode Input
;------------ Allow for Operating System Differences ---------------
; The following values are fine for my versions of Vista and XP, both with
; dpi = 96. With other setups or fonts you may have to change these parameters
; to allow for different row heights within the ListView.
ScrollBarHt = 20
ColHeaderHt = 25
if A_OSVersion in WIN_VISTA
RowHeight = 17 ; good for Vista
Else
RowHeight = 14 ; good for XP
; Derive path to 'Recent Items'
If A_OSVersion In WIN_VISTA ; Win7 is reputed to respond as WIN_VISTA as well but I can't test it.
{
EnvGet , Path , AppData
Target = %Path%\Microsoft\Windows\Recent
}
Else ; (Tested only for XP and Win2000)
{
EnvGet , Path , UserProfile
Target = %Path%\Recent
}
;------------------ Service Click on Tray Icon-------------------
; Based on http://www.autohotkey.com/forum/topic36960.html by Serenity.
OnMessage(0x404, "AHK_NOTIFYICON")
AHK_NOTIFYICON(wParam, lParam)
{
Global click,IconClick
If lParam = 0x202 ; WM_LBUTTONUP
{
IconClick=1
gosub, OnHotKey ;OnIconClick
}
IconClick=
Return
}
;----------------------------Prepare ini file-----------------------
IniFileName = %A_WorkingDir%\ShowRcntItems(%A_UserName%-%A_ComputerName%).ini
IfNotExist, %IniFileName%
{
DTypes = doc,pdf,txt,url,jpg ; Default types of file extensions to be included in list of recent items
IniWrite , 1 , %IniFileName% , Settings , NewestFirst ; If '1' newest is at top
IniWrite , 100 , %IniFileName% , Settings , xloc
IniWrite , 100 , %IniFileName% , Settings , yloc
IniWrite , 616 , %IniFileName% , Settings , Width
IniWrite , 760 , %IniFileName% , Settings , Height
IniWrite , 1 , %IniFileName% , Settings , Monitor
IniWrite , %DTypes% ,%IniFileName% , Settings , SelectedTypes
IniWrite , %A_Space% , %IniFileName% , Settings , WeeksAgo
IniWrite , 3 , %IniFileName% , Settings , MinFrequ ; Must be >(1+MinFrequ) of a Type to warrant a tick box
IniWrite , 20 , %IniFileName% , Settings , MaxBoxes
IniWrite , %A_Space% , %IniFileName% , Settings , FilterOn
IniWrite , 1 , %IniFileName% , Settings , Include
IniWrite , %A_Space% , %IniFileName% , Settings , SortByType
IniWrite , Alt+x , %IniFileName% , Settings , HotKey
}
Gosub , ReadIni
Gosub , DefineHelpMessages
Gosub , DoTray
Gosub , DoContext
Gosub , SetupHotkey
;---------------------- Show gui for first time ------------------
SelectionBoxes := GetUsedTypes(Target) ; Get box titles based on analysis of Recent Items
gosub , Main ; Generate gui at start-up and display it for first time
Gosub , DeriveFilterOn ; Revise display so WeeksAgo=0 can disable filter settings
Return ; Wait for hotkey or a click on the icon
;----------------- Define the Hotkey and service it ----------------------
SetupHotkey:
Toggle = 1 ; ensures first use of hotkey hides the gui
HKey =
Counter =
NonModifiers =
loop , parse , HotKey , + , %A_Space% ; ignore spaces
{
X:=Convert(A_LoopField) ; Convert modifier names to ahk symbols
HKey = %HKey%%X%
Counter +=1
}
If (Counter>=2 And NonModifiers=1) ; Must have at least one modifier plus exactly one non-modifier.
Hotkey , %HKey% , OnHotKey ; Declare the hotkey
Else
HKey = ; Invalid definition so no hotkey set.
Return
OnHotKey:
; If window exists but not in foreground, bring it to the fore, else hide it.
; Means only one action needed whether or not the window is hidden behind another.
; Unfortunately can't seem to do this for icon click as the act of clicking the
; icon steals focus from the gui.
If !Toggle
{
gosub , StoreGuiData
Gui , Restore
Gosub , Resize
Toggle ^=1 ; complement the flag
}
Else
{
If !IconClick
{
IfWinNotActive, %A_ScriptName%
{
WinActivate, %A_ScriptName%
Return
}
}
gosub , hide ; subroutine includes complementing the flag
}
Return
;Translate Hotkey Symbols
Convert(Symbol)
{
Global NonModifiers
IfEqual , Symbol, Ctrl , Return , "^"
IfEqual , Symbol, Alt , Return , "!"
IfEqual , Symbol, Shift , Return , "+"
IfEqual , Symbol, Win , Return , "#"
IfEqual , Symbol, LCtrl , Return , "<^"
IfEqual , Symbol, LAlt , Return , "<!"
IfEqual , Symbol, LShift , Return , "<+"
IfEqual , Symbol, LWin , Return , "<#"
IfEqual , Symbol, RCtrl , Return , ">^"
IfEqual , Symbol, RAlt , Return , ">!"
IfEqual , Symbol, RShift , Return , ">+"
IfEqual , Symbol, RWin , Return , ">#"
Else
NonModifiers +=1
Return , Symbol
}
;----------------------------------------------
;---------------- Get data from ini file----------------------------------
ReadIni:
FileRead , Ini , %IniFileName% ; Load working copy of ini file
NewestFirst := ini_getValue(ini, Settings, "NewestFirst")
xloc := ini_getValue(ini, Settings, "xloc")
yloc := ini_getValue(ini, Settings, "yloc")
Width := ini_getValue(ini, Settings, "Width")
Height := ini_getValue(ini, Settings, "Height")
Mon := ini_getValue(ini, Settings, "Monitor")
MinFrequ := ini_getValue(ini, Settings, "MinFrequ")
MaxBoxes := ini_getValue(ini, Settings, "MaxBoxes")
WeeksAgo := ini_getValue(ini, Settings, "WeeksAgo")
SelectedTypes:= ini_getValue(ini, Settings, "SelectedTypes")
FilterOn := ini_getValue(ini, Settings, "FilterOn")
Include := ini_getValue(ini, Settings, "Include")
SortByType := ini_getValue(ini, Settings, "SortByType")
HotKey := ini_getValue(ini, Settings, "HotKey")
Return
;----------------- Assemble the GUI --------------------------
Main:
gui, font, s8, Tahoma
Gui , +Toolwindow ; +AlwaysOnTop ; Use narrower title bar and no presence on the TaskBar
Gui, Add, StatusBar
If HKey
SB_SetText("To toggle the window use the hotkey " . hotkey . ", or click the tray icon.`t`tRight-click the tray icon for more information.")
Else
SB_SetText("To toggle the window left-click the tray icon.`t`tRight-click the tray icon for more information.")
Gui , Margin , 5
Gui, Add, GroupBox, y0 w120 h60,
Gui, Add, GroupBox, x140 yp w390 hp,
Reject := Include^1
gui , add , Radio , xm+5 ym+35 Checked%Include% gIncl vIncl, Include
gui , add , Radio , xp+60 yp Checked%Reject% gIncl, Reject
gui , add , Button , xm+5 ym+5 h20 vClr gButtonClr , Clr Ticks ; w20
gui , add , Button , xp+60 yp hp vAll gButtonAll , Tick All
/*
gui , add , Button , xm+5 ym+5 h20 vAll gButtonAll , Tick All
gui , add , Button , xp+60 yp hp vClr gButtonClr , Clr Ticks ; w20
*/
; Add checkboxes
loop, parse , SelectionBoxes , `,
{
;If !A_LoopField
; continue
If (A_Index > MaxBoxes)
Break
Temp =
If A_LoopField In %SelectedTypes%
Temp = Checked ; Box starts off ticked if Temp = Checked
If A_Index =1
gui , add , Checkbox , x150 ym+10 %Temp% gIncl vBox%A_Index%, %A_LoopField%
If A_Index between 2 and 10
gui , add , Checkbox , x+ yp %Temp% gIncl vBox%A_Index%, %A_LoopField%
If A_Index =11
gui , add , Checkbox , x150 ym+30x %Temp% gIncl vBox%A_Index%, %A_LoopField%
If A_Index between 12 and 20
gui , add , Checkbox , x+ yp %Temp% gIncl vBox%A_Index%, %A_LoopField%
}
; Add other controls
gui , add , Edit , r1 limit2 Number xm+540 w20 ym+5 gWeeksAgo vWeeksAgo , %WeeksAgo%
gui , add , text ,yp+3 x+5 , Weeks
; Add invisible default button so Enter refreshes display (eg to resize after moving to another monitor)
gui , add , Button , yp w20 h20 gIncl vRefresh Default , Refresh
GuiControl , Hide , Refresh
Gui, Add, ListView, AltSubmit x5 r2 w600 gMyListView vMyListView, RawTime|Link|File Name|Type|Accessed|Folder
LV_ModifyCol(1, 0) ; hide col 1
LV_ModifyCol(2, 0) ; hide col 2
LV_ModifyCol(3, 275) ; set fixed width
LV_ModifyCol(5, 70) ; set fixed width
LV_ModifyCol(4, "NoSort") ; Sorting by this column is handled in MyListView:
Gui , Show , X%xloc% Y%yloc% Hide ; Keep hidden until ListView rows added
Gosub , GetList ; compile RecentItems list
Gosub , DoListView ; Add rows to the ListView
Gosub , Resize ; Resize and show the gui
return
;----------This section compiles a list of the required shortcuts--------------------
; Gets all the shortcuts from 'My Recent Documents' that match the selection criteria
; and are not folders, sorts them so the most recent is first, and counts them so ListView
; knows how many rows to reserve. The loop looks at all recent items so needs to be fast.
; Notes
; 1. Most shortcuts are like this: 'examplefile.doc.lnk'. However duplicate
; shortcuts to the same file have the form 'examplefile.doc (2).lnk'. These are discarded.
; 2. In Vista all items in Recent Items have the extension .lnk, such as
; *.doc.lnk and *.url.lnk. XP is different in that urls are stored directly
; as *.url, and not as *.url.lnk.
GetList:
RecentItems =
Rows=
Loop , %Target%\*
{
If A_LoopFileExt not in lnk,url ; ignore such as *.ini and *.db
Continue
LinkTitle = %A_LoopFileName% ; such as 'sometext.doc.lnk', or in XP only, 'sometext.url'
; If present, strip off '.lnk' and discard if no '.' remains in the filename (must be a folder).
If A_LoopFileExt = lnk
{
StringTrimRight , LinkTitle , LinkTitle , 4 ; remove '.lnk'
IfNotInString , LinkTitle , `.
Continue
}
; Discard duplicates like 'sometext.doc (2).lnk' or 'sometext (2).url'.
; Note: 'sometext (2).url' could occur in XP but not in Vista
If RegExMatch(A_LoopFileName, "i)\s\(\d+\)\.\w{3}$")
Continue
If FilterOn ; Filtering is turned off if 'Weeks' below a threshold
{
SplitPath , LinkTitle ,,, FileType
If !Include ; If 'Reject' is selected
{
If FileType Contains %SelectedTypes%
Continue
}
Else If Include ; If 'Include' is selected
{
If FileType Not Contains %SelectedTypes%
Continue
}
}
RecentItems = %RecentItems%%A_LoopFileTimeModified%|%A_LoopFileLongPath%`n ; NB 'Time' is when the link was last modified
; RecentItems = %RecentItems%%A_LoopFileTimeAccessed%|%A_LoopFileLongPath%`n ; NB 'Time' is when the link was last accessed.
Rows +=1
}
sort , RecentItems , N R ; Put newest first
StringTrimRight , RecentItems , RecentItems , 1 ; remove final `n
Return
; ----------------------- Add the ListView-----------------------------------
; Scans each item in the RecentItems list and displays it as a row in the ListView.
; The columns RawTime and Link are hidden. When the user click on the column
; 'Accessed', it actually sorts on RawTime. The contents of Link are used when the
; user left-clicks on a row to activate the selected shortcut.
DoListView:
LV_Delete() ; clear existing rows
Entries = 0
MaxLength = 45
loop , parse , RecentItems , `n
{
StringSplit , Temp , A_LoopField , | ; Temp1 holds time accessed, Temp2 holds address of Shortcut
; Apply age restrictions
If WeeksAgo < 99 ; Apply no restriction at all
{
; If Temp1 is older than WeeksAgo, stop adding items to the list
CutOffTime := A_Now ; Current time in YYYYMMDDHH24MISS format
If !WeeksAgo
HoursAgo := 48
Else
HoursAgo := 7*24*WeeksAgo
EnvAdd , CutOffTime , -%HoursAgo% , H
EnvSub , CutOffTime , Temp1 , H
If CutOffTime >= 1
Break
}
RawTime = %Temp1% ; in YYYYMMDDHH24MISS format
FormatTime , Time , %Temp1% , dd-MM-yyyy ; Formatted time in 'Time'
If Temp2 Contains .url ; Must be an Internet shortcut
{
SplitPath , Temp2 , , OutDir, Type ,Tmp1
LV_Add("" , RawTime , Temp2, Tmp1 , "url" , Time, "" ) ; Display the data.
}
Else ; Must be a shortcut to a file
{
FileGetShortcut , %Temp2% , FilePath ;,,,, OutIcon , OutIconNum ; get full path of target file
SplitPath , FilePath , FileName , OutDir, Type
If IsFolder(FilePath) ; Don't add the row if the item is a folder
Continue
LV_Add("", RawTime, Temp2, FileName, Type, Time, OutDir) ; Add the data
}
Entries +=1
}
If NewestFirst = 1
LV_ModifyCol(1, "SortDesc")
If NewestFirst = 0
LV_ModifyCol(1, "Sort")
; Do extra sort on Type if this mode is selected
If SortByType
Gosub , SortByType
Return
; Function returns 1 if FileName is a folder, blank otherwise.
IsFolder(FileName)
{
FileGetAttrib , Attributes , %FileName%
IfInString, Attributes, D ; Directory
Result = 1
Else
Result =
Return %Result%
}
;-------------- Get box titles ------------------
; Analyses Recent Items and makes a list of the different file types, ordered
; with the most frequent type first. Ignore items which appear only once.
GetUsedTypes(Target)
{
Global MinFrequ
Types =
Rows=
Loop , %Target%\*
{
If A_LoopFileExt not in lnk,url ; ignore such as *.ini and *.db
Continue
LinkTitle = %A_LoopFileName% ; such as 'sometext.doc.lnk', or in XP only, 'sometext.url'
; If present, strip off '.lnk' and discard if no '.' remains in the filename (must be a folder).
If A_LoopFileExt = lnk
{
StringTrimRight , LinkTitle , LinkTitle , 4 ; remove '.lnk'
IfNotInString , LinkTitle , `.
Continue
}
; Discard duplicates like 'sometext.doc (2).lnk' or 'sometext (2).url'.
If RegExMatch(A_LoopFileName, "i)\s\(\d+\)\.\w{3}$")
Continue
SplitPath , LinkTitle ,,, FileType
; Check link is to a file and not a folder
FileGetShortcut , %A_LoopFileFullPath% , FilePath
If IsFolder(FilePath) ; Only use if it's a folder
Continue
If FileType
Types = %Types%%FileType%|
Rows +=1
}
sort , Types , D|
NewList =
Count =1
Loop , parse , Types , |
{
If (A_LoopField = PreviousItem) ; It's a duplicate
Count +=1
Else ; It's a new item, so append the previous one
{
Count := SubStr("000" . Count, -3) ; pad to 3 digits
If (PreviousItem And Count>=MinFrequ) ; Store only if type appears at least MinFrequ times
{
StringLower , PreviousItem , PreviousItem
NewList = %NewList%%PreviousItem%\%Count%`n
}
Count =1
}
PreviousItem = %A_LoopField%
}
sort , NewList , \ R ; put most frequently-used extension first
NewList := RegExReplace(NewList , "\\\d{4}\`n" , ",")
StringTrimRight , NewList , NewList , 1
Return , NewList
}
;---------------------------------------------------------
; Resize ListView and Gui to reflect number of rows
Resize:
IfWinExist , %A_ScriptName% ; On first run there's no window so uses data from ini
Gosub ,GetWinParameters
sysget , Mon, MonitorWorkArea , %Mon% ; get MonBottom, etc
UsableHeight := MonBottom - MonTop -50 ; Delete '-50' if no free space preferred
LViewHeight := ScrollBarHt + ColHeaderHt + Entries*RowHeight ; Target Height for ListView
If (LViewHeight +120 > UsableHeight)
LViewHeight := UsableHeight - 200 ;120
GuiControl, Move, SysListView321 , H%LViewHeight%
Gui , Show , X%xloc% Y%yloc% AutoSize
LV_ModifyCol(4, "AutoHdr") ; Autosize the Type column
LV_ModifyCol(6, "AutoHdr") ; Autosize the Folder column
Gosub , AutoPosition
Return
;----------- Auto-position the GUI-----------------
AutoPosition:
Gosub ,GetWinParameters
sysget , Mon, MonitorWorkArea , %Mon% ; stores MonBottom, etc
If (BottomEdge > MonBottom)
{
yloc := (MonBottom-Height) ; Put bottom edge on TaskBar top
HasBottomed = 1 ; set flag
}
Else If HasBottomed
{
yloc := (MonBottom-Height)/2 ; To have gui centralised vertically
HasBottomed = ; clear flag
}
WinMove , %A_ScriptName% ,, %xloc% , %yloc%
Return
GetWinParameters:
WinGetPos , xloc , yloc, Width , Height, %A_ScriptName%
BottomEdge := yloc+Height
; Determine which monitor contains the center of the window.
Mon := GetMonitorAt(xloc+Width/2, yloc+Height/2)
Return
;------------ Code from WindowPad by Lexikos -----------
; Get the index of the monitor containing the specified x and y co-ordinates.
GetMonitorAt(x, y, default=1)
{
SysGet, m, MonitorCount
; Iterate through all monitors.
Loop, %m%
{ ; Check if the window is on this monitor.
SysGet, Mon, Monitor, %A_Index%
if (x >= MonLeft && x <= MonRight && y >= MonTop && y <= MonBottom)
return A_Index
}
return default
}
;-----------------------------------------------
;------------------ Get Path From Link --------------
; Input link and get full path and extension of target.
;
; Example: Target := GetFilename(Link)
;
GetFilename(Link)
{
FileGetShortcut , %Link% , OutTarget ; OutTarget holds full path and extension of target file
SplitPath , OutTarget , OutFileName , OutDir ;, OutExtension, OutNameNoExt, OutDrive
FullFileName = %OutDir%\%OutFileName%
Return, FullFileName ; filename with extension but no path
}
DeriveFilterOn:
If !(WeeksAgo>0) ; ie no filtering if box contains 0 or blank
{
FilterOn =
loop, parse , SelectionBoxes , CSV
GuiControl , disable , Box%A_Index%
GuiControl , disable , Include
GuiControl , disable , Reject
GuiControl , disable , All
GuiControl , disable , Clr
}
Else
{
FilterOn = 1
loop, parse , SelectionBoxes , CSV
GuiControl , enable , Box%A_Index%
GuiControl , enable , Include
GuiControl , enable , Reject
GuiControl , enable , All
GuiControl , enable , Clr
}
Return
/*
DeriveFilterOn:
If !(WeeksAgo>=1)
{
FilterOn =
loop, parse , SelectionBoxes , CSV
GuiControl , disable , Box%A_Index%
GuiControl , disable , Include
GuiControl , disable , Reject
GuiControl , disable , All
GuiControl , disable , Clr
}
Else
{
FilterOn = 1
loop, parse , SelectionBoxes , CSV
GuiControl , enable , Box%A_Index%
GuiControl , enable , Include
GuiControl , enable , Reject
GuiControl , enable , All
GuiControl , enable , Clr
}
Return
*/
;-------------- Service the gui buttons ----------------------
; Updates data and gui after user clicks a Checkbox or a Radio Button
Incl:
GuiControlGet , Include ,, Incl
GuiControlGet , WeeksAgo ,, WeeksAgo
Gosub , DeriveFilterOn
; Read the ticks in the check boxes
SetTitleMatchMode, 3 ; Required by ControlGet else, eg, 'd' is confused with 'doc'.
SelectedTypes =
loop , parse , SelectionBoxes , CSV
{
ControlGet , Temp , Checked ,, %A_LoopField%, %A_ScriptName%
If Temp
SelectedTypes = %SelectedTypes%%A_LoopField%,
}
SetTitleMatchMode, 1 ; Restore to default
gosub , StoreGuiData
Gosub , GetList ; compile RecentItems list and get required number of rows
Gosub , DoListView
Gosub , Resize ; Resize ListView and GUI
Return
; Ticks all the boxes
ButtonAll:
SelectedTypes = %SelectionBoxes%
;Include = 0
Tick = 1
Gosub , RefreshButtons
Gosub , Resize ; Resize ListView and GUI
Return
; Clears ticks from all the boxes
ButtonClr:
SelectedTypes =
;Include = 1
Tick = 0
Gosub , RefreshButtons
Gosub , Resize ; Resize ListView and GUI
Return
; Refresh the radio buttons and checkboxes
RefreshButtons:
GuiControl , , Include , %Include%
GuiControl , , Reject , % Include^1
loop, parse , SelectionBoxes , CSV
GuiControl , , Box%A_Index% , %Tick%
gosub , StoreGuiData
Gosub , GetList ; compile RecentItems list and get required number of rows
Gosub , DoListView
Return
; Updates the locally stored copy of the ini file
StoreGuiData:
WinRestore , %A_ScriptName%
IfWinExist , %A_ScriptName% ; In case gui not present on exit
{
;WinGetPos , xloc, yloc ,width,height, %A_ScriptName%
Gosub ,GetWinParameters
ini_replaceValue(ini, Settings, "xloc", xloc)
ini_replaceValue(ini, Settings, "yloc", yloc)
ini_replaceValue(ini, Settings, "width", width)
ini_replaceValue(ini, Settings, "height", height)
ini_replaceValue(ini, Settings, "Monitor", Mon)
}
ini_replaceValue(ini, Settings, "NewestFirst", NewestFirst)
ini_replaceValue(ini, Settings, "MinFrequ", MinFrequ)
ini_replaceValue(ini, Settings, "MaxBoxes", MaxBoxes)
ini_replaceValue(ini, Settings, "WeeksAgo", WeeksAgo)
ini_replaceValue(ini, Settings, "SelectedTypes", SelectedTypes)
ini_replaceValue(ini, Settings, "SortByType", SortByType)
ini_replaceValue(ini, Settings, "FilterOn", FilterOn)
ini_replaceValue(ini, Settings, "Include", Include)
ini_save(ini, IniFileName) ; Update the ini file from the locally stored version
Return
;----------- Shut down procedures --------------
OnExit , ExitSub
ExitSub:
Gosub , StoreGuiData
ini := ini_repair(ini, False, ";#", "`r`n") ; allows for possible loss of CR after GuiControlGet
ini_save(ini, IniFileName) ; Update the ini file from the locally stored version
ExitApp
GuiClose:
GuiEscape:
Hide:
LV_Modify(A_ThisMenuItem, "-Select") ; Remove highlighting from last activated row in ListView
LV_Modify(A_ThisMenuItem, "-Focus")
gosub , StoreGuiData
Toggle ^= 1 ; Ensure gui appears on next hotkey press
gui , hide
Return
;----------------- Service user-input to the GUI --------------
Rows:
WeeksAgo:
SetTimer , LoadRows , -100
Return
LoadRows:
gosub , Incl
SortByType:
LV_ModifyCol(4, "-NoSort")
If SortByType = 1
LV_ModifyCol(4, "Sort")
If SortByType = 2
LV_ModifyCol(4, "SortDesc")
LV_ModifyCol(4, "NoSort")
Return
MyListView:
Event = %A_EventInfo%
If A_GuiEvent=ColClick
{
If Event=5 ; Divert to sorting on 'raw time'
{
SortByType = ; Cancel 'Sort on Type' mode
NewestFirst ^=1
If NewestFirst = 1
LV_ModifyCol(1, "SortDesc")
If NewestFirst = 0
LV_ModifyCol(1, "Sort")
}
If Event=4
; Set 'Sort By Type' mode, toggle between 2 and 3, and do a sort on Type
{
NewestFirst = 0 ; Reset flag for normal sort (it will be complemented before use)
SortByType += 1
If SortByType > 2
SortByType -= 2
Gosub , SortByType
}
}
Else if A_GuiEvent = Normal ; A Left-click on a row
{
LV_GetText(Type, Event , "4") ; Get target file extension
LV_GetText(Link, Event , "2") ; Get Link
LV_GetText(Folder, Event , "6") ; get target path
LV_GetText(DisplayedName, Event , "3") ; get displayed File Name
;LV_Modify(A_ThisMenuItem, "-Select") ; Remove highlighting from selected row
;LV_Modify(A_ThisMenuItem, "-Focus")
Gosub , OpenLink
}
Return
;--------------- Context Menu ----------------------
; Create a popup menu to be used as the context menu:
DoContext:
Menu, MyContextMenu, Add, Open, OpenLink
Menu, MyContextMenu, Add, Explore , ContextExplore
Menu, MyContextMenu, Add, Edit , ContextEdit
Menu, MyContextMenu, Add, Print , ContextPrint
Menu, MyContextMenu, add ; Creates a separator line.
Menu, MyContextMenu, Add, Find, ContextFind
Menu, MyContextMenu, Add, Remove Link, ContextRemove
Menu, MyContextMenu, Add, Properties, ContextProperties
Menu, MyContextMenu, Default, Open ; Make "Open" a bold font to indicate that double-click does the same thing.
return
GuiContextMenu: ; Launched in response to a right-click or press of the Apps key.
if A_GuiControl <> MyListView ; Display the menu only for clicks inside the ListView.
return
Event = %A_EventInfo%
LV_GetText(Type, Event , "4") ; Get target file extension
LV_GetText(Link, Event , "2") ; Get Link
LV_GetText(Folder, Event , "6") ; get target path
LV_GetText(DisplayedName, Event , "3") ; get displayed File Name
; For all except url links, show the menu at the provided coordinates, A_GuiX and A_GuiY.
; Open url links without showing a menu.
;LV_Modify(A_ThisMenuItem, "-Select") ; Remove highlighting from selected row
;LV_Modify(A_ThisMenuItem, "-Focus")
If Type in url
Gosub , OpenUrl
Else
Menu, MyContextMenu, Show, %A_GuiX%, %A_GuiY%
return
OpenLink: ; The user selected "Open" in the context menu.
If Type not in url ; if extension not 'url', check selected file exists
{
filename := GetFilename(Link)
If !FileExist(filename)
{
BadLinkOffer(Event)
Return
}
}
Gosub , OpenUrl
Return
OpenUrl:
Run , %Link% , , UseErrorLevel
If ErrorLevel
{
BadLinkOffer(Event) ; not tested properly here yet - only needed in Vista
Return
}
Gosub, Hide
Return
ContextProperties:
ContextExplore:
ContextEdit:
ContextPrint:
ContextFind:
ContextRemove:
filename := GetFilename(Link)
If !FileExist(filename)
{
BadLinkOffer(Event)
Return
}
IfInString A_ThisMenuItem, Explore ; User selected "Explore" from the context menu.
Run %Folder%,, UseErrorLevel
Else IfInString A_ThisMenuItem, Properties
Run Properties "%filename%",, UseErrorLevel
Else IfInString A_ThisMenuItem, Edit
Run Edit "%filename%",, UseErrorLevel
Else IfInString A_ThisMenuItem, Print
Run Print "%filename%",, UseErrorLevel
Else IfInString A_ThisMenuItem, Remove
{
MsgBox, 262468, , "%filename%"`r`rThis will remove the link to the above file from the Recent Items folder.`rIt will not delete or affect the actual file.`r`rPress Yes to delete the link, or press No to cancel.
IfMsgBox Yes
{
FileDelete , %Link%
If ErrorLevel
{
MsgBox, 262208, , "%Link%"`r`rUnable to delete the above link.
Return ; without closing gui
}
Gosub , Incl ; Refresh the display
Return
}
Return ; without closing gui
}
Else IfInString A_ThisMenuItem, Find
{
Run , "%ProgramFiles%\Everything\Everything.exe" -search "%DisplayedName%", %ProgramFiles%\Everything\ , UseErrorLevel
If ErrorLevel
MsgBox , Cannot search for files because the search program "%ProgramFiles%\Everything\Everything.exe" is not present.`n`n("Everything" is available as a free download from http://www.voidtools.com/).
Return ; without closing gui
}
If ErrorLevel
{
MsgBox, 262208, , Command failed to work
Return ; without closing gui
}
Gosub, Hide
Return
;---------------------
; Offers chance to delete a bad link and searches for the file at other locations.
; The latter requires that Everything.exe is installed at its default location.
;
; Example: BadLinkOffer(RowNumber)
;
BadLinkOffer(RowNumber)
{
global Filename,DisplayedName,Link
IfExist , %ProgramFiles%\Everything\Everything.exe
{
Run , "%ProgramFiles%\Everything\Everything.exe" -search "%DisplayedName%", %ProgramFiles%\Everything\
MsgBox, 262468, , "%filename%"`r`rThe link to the above file in Recent Items is invalid.`nNow searching for the file in case it was moved elsewhere.`n`nMeanwhile press Yes to delete the faulty link, or press No to finish.
}
Else
MsgBox, 262468, , "%filename%"`r`rThe link to the above file in Recent Items is invalid.`rPress Yes to delete the faulty link, or press No to finish.
IfMsgBox Yes
{
; LV_GetText(Link, RowNumber , "2") ; Get the Shortcut
FileDelete , %Link%
Gosub , Incl ; Refresh the display
Return , Errorlevel
}
Return , Errorlevel
}
;----------------------------------------------
; Define Tray properties
DoTray:
Menu, Tray, NoStandard
Menu , Tray, Icon , Shell32.dll, 21
Menu, Tray, Tip, %A_ScriptName% - by DAT
Menu, Tray, add, What it Does, Description
Menu, Tray, add, User Guide, UserInfo
Menu, Tray, add, How to change the hotkey (currently %HotKey%), ChangeHotkey
Menu, Tray, add ; Creates a separator line.
Menu, Tray, add, Change the default settings, OpenIni
Menu, Tray, add, Restore the original settings, ResetIni
Menu, Tray, add ; Creates a separator line.
Menu, Tray, add, About This Program, About
Menu, Tray, add, Exit, ExitSub
Return
Description:
msgbox ,256, Description, %DescriptionMsg%
Return
UserInfo:
msgbox ,256, Instructions, %UserInfoMsg%
Return
ChangeHotkey:
msgbox ,256, Instructions, %ChangeHotkey%
Return
About:
msgbox ,256, About This Program, %AboutMsg%
Return
OpenIni:
Gosub , StoreGuiData
ini := ini_repair(ini, False, ";#", "`r`n") ; allows for possible loss of CR after GuiControlGet
ini_save(ini, IniFileName) ; Update the ini file from the locally stored version
Runwait , Notepad %IniFileName%
Reload
Return
ResetIni:
MsgBox, 262449, , Press OK if you really want to delete all current settings for %A_ScriptName% and restore them to their default values. `n`nOtherwise press Cancel to abort.`n.,
IfMsgBox , Cancel
Return
Else
{
FileDelete , %IniFileName%
If ErrorLevel
msgbox ,262208, , %IniFileName% file could not be deleted. (It may have been deleted already).
Reload
}
Return
DefineHelpMessages:
DescriptionMsg =
(
What it Does:
1. The program gives quick access to recently-used files and folders and presents them in an Explorer-like window with rapid sorting facilities.
2. Windows stores shortcuts to recently-used files in the special folder 'My Recent Documents'. The program lets you filter this collection so you can find the one(s) you want very quickly. Once you've located an item in the display, you can left-click to open it, or right-click to choose various other options.
3. If the link is no longer valid because the file has been deleted or moved, the program gives you the option to delete the shortcut. In case the file was moved, it also searches for the file using Everything.exe (assuming you have this installed).
4. Once launched, it's important to leave the program running. You can then open or close its display window by left-clicking the Tray icon or by pressing a predefined hotkey.
)
UserInfoMsg =
(
Basics:
1. Left-click the Tray icon to toggle the display window on and off, or use the hotkey combination which by default is Alt-x. (This means 'hold down Alt and press x'). You can also hide the window by pressing Escape or with the normal Close button.
2. If the item was accessed in the past 48 hours, enter 0 or blank into the 'Weeks' box. (This setting disables the other filters). Otherwise enter the number of weeks back from today that you want to cover. If you enter 99 it shows everything with no time restriction.
3. To filter the search for particular types of file, tick the appropriate boxes and either 'Include' or 'Reject' them. The display refreshes with each input or if you press Enter.
4. By default results are sorted with newest at the top. Otherwise click the column header 'Accessed' to toggle between newest at the top or newest at the bottom. To sort on file type as well, click column header 'Type' for alphabetical order, and again for reverse alphabetical. Sorting jointly by type and date continues (even after restarting) until the next time you click the Accessed header to sort by date. (You can also sort on the 'File Name' and 'Folder' columns but these settings are not stored).
5. When you've spotted the item you want you can left-click the row to activate the link, or right-click it for more options, pretty much as in Explorer.
6. Right-click options are: Open (same as left-clicking the row), Explore, Edit, Print, Find, Remove Link and Properties. If the item is a web link (url), a right-click does the same as a left-click (opens the web page in the browser). Remove Link asks for confirmation and then deletes the link from Recent Items. It does not affect the actual target file.
7. The Find option searches the computer for the name in the 'File Name' column. For files such as 'ReadMe.txt' it may find many other files with the same name but different paths. (The Search function requires that Everything.exe is installed. You can get this excellent free search tool from http://www.voidtools.com/).
7. To shut down the program completely, right-click on the Tray icon and select 'Exit'.
Hints:
1. You may find it useful to select enough boxes to include about half of your favourite files, but leaving out an especially dominant type such as 'url'. This gives less clutter when you press Include, and yet you can still view all the others just by clicking Reject. Tip: you can tick all the boxes at once, or clear them all at once with the buttons 'Tick All' and 'Clr Ticks'.
2. By default, results are sorted by date. If you then click the Type header, a further sort into groups of the same type occurs, but within each group the items remain in the previous date order. Clicking Type toggles between alphabetical and reverse alphabetical sorting. Once Type has been clicked, sorting jointly by Type and date continues until you next click the Accessed header when it reverts to sorting by date only.
3. When the program first runs it analyses the number of each file type in Recent Items. It assigns the boxes so that the most frequent is at top left and the least frequent at bottom right. A type must appear at least 3 times to merit a box, and up to 20 boxes can be shown. (Both of these parameters, MinFrequ and MaxBoxes respectively, can be altered by editing the ini file. Current values are %MinFrequ% and %MaxBoxes%)
)
ChangeHotkey=
(
Changing the Hotkey:
1. The default hotkey combination is "Alt+x", which means 'hold down either of the Alt keys and type x'. Alternatives to the 'modifier' Alt are Ctrl, Shift, or Win, and more than one may be used in cascade. The 'modifiers' must be followed by any single character from A to Z, 0 to 9, or F1 to F12, none of which are case-sensitive. When the computer has say two Alt keys, you can specify to use the left or right key by using LAlt or RAlt. The same applies to the other modifiers so, for example, LAlt+RCtrl+p means 'hold down the left Alt key and the right Ctrl key and type p'.
2. To change the hotkey you need to edit the settings file (aka "ini file") that the program puts into its folder when you first run it. Alter the line "HotKey=Alt+x" to suit your preference. If you don't want to use a hotkey, leave the entry blank, eg "HotKey= ". Any spaces will be ignored, but be sure to put a "+" between each item.
3. To open the ini file, right-click the icon and select 'Change the default settings'. The file will open in Notepad and you can edit as required. When you've finished, close the file and accept to store the changes. The program will then restart and load the new settings. (The ini file on this installation is %IniFileName%).
4. You can restore to the default settings by right-clicking the icon and selecting 'Restore original settings'.
)
AboutMsg =
(
"%A_ScriptName%". Written by David Tong.
This script has been tested with Vista, XP, Win2000.
Acknowledgements:
1. The program uses some of Tuncay's ini functions published at http://www.autohotkey.com/forum/viewtopic.php?t=46226.
2. The method of detecting a click on the Tray icon is by Serenity at http://www.autohotkey.com/forum/topic36960.html
3. The function GetMonitorAt() is from WindowPad by Laszlo.
)
Return
;-------------------- Below: a subset of Tuncay's Ini Functions (see Acknowledgements)-------------
; Extracted from download by Tuncay at http://www.autohotkey.com/forum/viewtopic.php?t=46226
ini_getValue(ByRef _Content, _Section, _Key, _PreserveSpace = False)
{
If (_Section = "")
_Section = (?:\[.*])?
Else
{
_Section = \[\s*?\Q%_Section%\E\s*?]
}
; Note: The regex of this function was rewritten by Mystiq.
RegEx = `aiU)(?:\R|^)\s*%_Section%\s*(?:\R\s*|\R\s*.+\s*=\s*.*?\s*(?=\R)|\R\s*[;#].*?(?=\R))*\R\s*\Q%_Key%\E\s*=(.*)(?=\R|$)
/*
RegEx := "`aiU)"
. "(?:\R|^)\s*" . _Section . "\s*" ;-- section
. "(?:"
. "\R\s*" ;-- empty lines
. "|\R\s*[\w\s]+\s*=\s*.*?\s*(?=\R)" ;-- OR other key=value pairs
. "|\R\s*[;#].*?(?=\R)" ;-- OR commented lines
. ")*"
. "\R\s*\Q" . _Key . "\E\s*=(.*)(?=\R|$)" ;-- match
*/
If RegExMatch(_Content, RegEx, Value)
{
If Not _PreserveSpace
{
Value1 = %Value1% ; Trim spaces.
FirstChar := SubStr(Value1, 1, 1)
If (FirstChar = """" AND SubStr(Value1, 0, 1)= """"
OR FirstChar = "'" AND SubStr(Value1, 0, 1)= "'")
{
StringTrimLeft, Value1, Value1, 1
StringTrimRight, Value1, Value1, 1
}
}
ErrorLevel = 0
}
Else
{
ErrorLevel = 1
Value1 =
}
Return Value1
}
ini_replaceValue(ByRef _Content, _Section, _Key, _Replacement = "", _PreserveSpace = False)
{
If (_Section = "")
_Section = (?:\[.*])?
Else
_Section = \[\s*?\Q%_Section%\E\s*?]
If Not _PreserveSpace
{
_Replacement = %_Replacement% ; Trim spaces.
FirstChar := SubStr(_Replacement, 1, 1)
If (FirstChar = """" AND SubStr(_Replacement, 0, 1)= """"
OR FirstChar = "'" AND SubStr(_Replacement, 0, 1)= "'")
{
StringTrimLeft, _Replacement, _Replacement, 1
StringTrimRight, _Replacement, _Replacement, 1
}
}
; Note: The regex of this function was written by Mystiq.
RegEx = `aiU)((?:\R|^)\s*%_Section%\s*(?:\R\s*|\R\s*.+\s*=\s*.*?\s*(?=\R)|\R\s*[;#].*?(?=\R))*\R\s*\Q%_Key%\E\s*=).*((?=\R|$))
_Content := RegExReplace(_Content, RegEx, "$1" . _Replacement . "$2", isReplaced, 1)
If isReplaced
ErrorLevel = 0
Else
ErrorLevel = 1
Return isReplaced
}
ini_save(ByRef _Content, _Path = "", _convertNewLine = true, _overwrite = true)
{
ini_buildPath(_Path)
error := false
If (_overwrite)
{
Loop, %_Path%, 0, 0
{
_Path := A_LoopFileLongPath
Break
}
If FileExist(_Path)
{
FileDelete, %_Path%
If (ErrorLevel)
{
error := true
}
}
}
Else If FileExist(_Path)
{
error := true
}
If (error = false)
{
If (_convertNewLine)
{
FileAppend, %_Content%, %_Path%
}
Else
{
FileAppend, %_Content%, *%_Path%
}
If (ErrorLevel)
{
error := true
}
}
ErrorLevel := error
Return _Path
}
ini_repair(_Content, _PreserveSpace = False, _CommentSymbols = ";#", _LineDelim = "`n")
{
If (_CommentSymbols != "")
{
regex = `aiUSm)(?:\R\s*|(s*|\t*))[%_CommentSymbols%].*?(?=\R)
_Content := RegExReplace(_Content, regex, "$1")
}
Loop, Parse, _Content, `n, `r
{
If (RegExMatch(A_LoopField, "`aiSm)\[\s*(.+?)\s*]", Match))
{
newIni .= _LineDelim . "[" . Match1 . "]"
section := Match1
KeyList := ""
}
Else If (RegExMatch(A_LoopField, "`aiSm)\s*(\b(?:.+?|\s?)\b)\s*=(.*)", Match))
{
If (_PreserveSpace = false)
{
Match2 = %Match2%
}
If Match1 Not in %KeyList% ; Disallowes dublicate.
{
KeyList .= "," . Match1
}
Else
{
; As a workaround it should be just deleted, because if set
; here the surrounding whitespaces are lost.
ini_replaceKey(newIni, section, Match1, "")
}
newIni .= _LineDelim . Match1 . "=" . Match2
}
}
StringReplace, newIni, newIni, %_LineDelim%
If (newIni != "")
{
ErrorLevel := 0
}
Else
{
ErrorLevel := 1
}
Return newIni
}
; An internally used function, made not for public.
ini_buildPath(ByRef _path)
{
; Set to default wildcard if filename or exension are not set.
If (_Path = "")
{
_Path := RegExReplace(A_ScriptFullPath, "S)\..*?$") . ".ini"
}
Else If (SubStr(_Path, 0, 1) = "\")
{
_Path .= RegExReplace(A_ScriptName, "S)\..*?$") . ".ini"
}
Else
{
If (InStr(FileExist(_Path), "D"))
{
; If the current path is a directory, then add default file pattern to the directory.
_Path .= "\" . RegExReplace(A_ScriptName, "S)\..*?$") . ".ini"
}
Else
{
; Check all parts of path and use defaults, if any part is not specified.
SplitPath, _Path,, fileDir, fileExtension, fileNameNoExt
If (fileDir = "")
{
fileDir := A_WorkingDir
}
If (fileExtension = "")
{
fileExtension := "ini"
}
If (fileNameNoExt = "")
{
fileNameNoExt := RegExReplace(A_ScriptName, "S)\..*?$")
}
_Path := fileDir . "\" . fileNameNoExt . "." . fileExtension
}
}
Return 0
}
ini_replaceKey(ByRef _Content, _Section, _Key, _Replacement = "")
{
If (_Section = "")
_Section = (?:\[.*])?
Else
_Section = \[\s*?\Q%_Section%\E\s*?]
If _Replacement !=
{
_Replacement = %_Replacement%
_Replacement = `n%_Replacement%
}
; Note: The regex of this function was written by Mystiq.
RegEx = `aiU)((?:\R|^)\s*%_Section%\s*(?:\R\s*|\R\s*.+\s*=\s*.*?\s*(?=\R)|\R\s*[;#].*?(?=\R))*)\R\s*\Q%_Key%\E\s*=.*((?=\R|$))
_Content := RegExReplace(_Content, RegEx, "$1" . _Replacement . "$2", isReplaced, 1)
If isReplaced
ErrorLevel = 0
Else
ErrorLevel = 1
Return isReplaced
}
;---------------------- End of Tuncay's function section --------------------------