AutoHotkey Community

It is currently May 27th, 2012, 11:50 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 7 posts ] 
Author Message
PostPosted: August 19th, 2010, 11:24 pm 
Offline

Joined: December 1st, 2008, 12:34 pm
Posts: 49
Location: UK
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.

Image

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.

Image

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 --------------------------   


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: August 21st, 2010, 1:59 am 
Havent tested but a reg-ex search would be awesome


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: August 21st, 2010, 5:26 pm 
Offline

Joined: December 1st, 2008, 12:34 pm
Posts: 49
Location: UK
widow wrote:
Havent tested but a reg-ex search would be awesome

Interesting idea - maybe I'll try adding a search-as-you-type box.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: August 21st, 2010, 11:49 pm 
Offline

Joined: December 1st, 2008, 12:34 pm
Posts: 49
Location: UK
widow wrote:
Havent tested but a reg-ex search would be awesome

Well thanks for that great idea, Widow! Adds a whole new dimension to it.

It's now got a search-as-you-type Edit box that accepts multiple search terms (3 at present as that seems ample). You just type and use a space as separator. Here's a screenshot as an example. (I typed two spaces between each to make the gaps more obvious - it just ignores the extras). The box always keeps focus so you can start typing as soon as the window pops up.

Image

I'm going to play with it for a bit and then reassess the layout of the controls.

Meanwhile if anyone wants to try it here's the interim new code.

[Edited 23-08-2010 16:39 to remove the interim code as the latest version is in the next post].


Last edited by DAT on August 23rd, 2010, 4:40 pm, edited 1 time in total.

Report this post
Top
 Profile  
Reply with quote  
PostPosted: August 23rd, 2010, 4:30 pm 
Offline

Joined: December 1st, 2008, 12:34 pm
Posts: 49
Location: UK
Quote:
I'm going to play with it for a bit and then reassess the layout of the controls.

I've rearranged the layout and added more features to make it more ergonomic. The screen shot shows an example of three very short search terms, but usually one or two are ample. It accepts up to three.

Note that the other controls are greyed out because there's something in the Search box. In this mode the other filters are turned off. Clear the search box and they come back into action again.

Image

Effectively it now offers two modes in one, depending on if there's anything in the search box. As per this extract from the 'User Guide' accessible via a right click on the tray icon:

"As soon as you enter something in the Search box, the other controls grey-out and other filtering is turned off, but if you clear the Search box the other controls come alive. You can then use the Incl or Rej buttons to Include or Reject the types of files selected by the check-boxes, and to set the depth of search to 1 to 98 weeks back from 'now'. If you set 99 into the 'Wks' box it turns off all time restrictions. If you make it 0 or blank, it selects the past 48 hours and greys out and turns off the other filters."

Here are links for the source code and for downloading a zip file with source code and compiled version.

I've checked it works fine on Vista, XP, W2k, but can anyone try it on Win7 for me?

Here's the full script:
Code:
 ;;;;;;;;;;;   
;     ShowRecentItems_DAT.ahk
;     Version 1.1
;     By David Tong, 23-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.

; --------------------------
;   Changes since Version 1.0
;   23-08-2010:   
;   Instructions updated to reflect search box.
;   
;   22-08-2010:   
;   1. Incl and Reject coupled to 'Tick All' and 'Clr Ticks' respectively. (Click All
;   shows everything not represented by the boxes, and click Clr shows nothing
;    until one or more boxes selected).    
;   2. When Search Box not empty, Type and Date filtering is cancelled and controls greyed out.
;   3. Reorganised the layout for better ergonomics.
;   4. Added MaxBoxes limit to GetUsedTypes().
;   5. Added Clear button for Search box.
;   
;   21-08-2010: 
;    Added search box for entering multiple search terms each separated by one or more spaces

; --------------------------
;   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 , 1 , %IniFileName% , Settings , WeeksAgo
   IniWrite , 3 ,       %IniFileName% , Settings , MinFrequ   ; Must be >(1+MinFrequ) of a Type to warrant a tick box
   IniWrite , 7 ,       %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
GuiControl , Focus , SearchBox
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 and
; clear the search box.

; 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.

GuiControl , Focus , SearchBox   ; useful always to put focus back here
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 , Margin , 5

Gui, Add, StatusBar         
If HKey
   SB_SetText("Type up to 4 search terms into box with a space between each.    Hotkey is " . hotkey . ".`t`tFor info right-click tray icon")
Else
   SB_SetText("Type up to 4 search terms into box with a space between each.     No hotkey set.`t`tFor info right-click tray icon")

gui , add , Edit , r1 Limit x5 ym+5 w100  gSearchBox vSearchBox
gui , add , Button , x+ yp h20 w15 vC, c      ; click to clear the contents of SearchBox
gui , add , Button , x+ yp w0 vDummy, Dummy      ; used to stop C button from retaining 'clicked' appearance

gui , add , Edit , r1 limit2 Number x+18 w20 yp gWeeksAgo vWeeksAgo , %WeeksAgo%
gui , add , text ,ym+9 x+3 vWks , Wks


Reject := Include^1
gui , add , Radio , x+10 yp Checked%Include% gIncl vIncl, Incl
gui , add , Radio , x+ yp  Checked%Reject% gIncl, Rej       

gui , add , Button , x+5 yp-5  h20  vClr gButtonClr , Clr ; w20
gui , add , Button , x+ yp  hp   vAll gButtonAll , All

; Add checkboxes
loop, parse , SelectionBoxes , `,      
{
   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 , xm+342 ym+8 %Temp%  gIncl vBox%A_Index%, %A_LoopField%
   If A_Index between 2 and 7
      gui , add , Checkbox , x+ yp %Temp%  gIncl vBox%A_Index%, %A_LoopField%
   If A_Index =8
      gui , add , Checkbox , xm+342 ym+28x %Temp%  gIncl vBox%A_Index%, %A_LoopField%
   If A_Index >8
      gui , add , Checkbox , x+ yp %Temp%  gIncl vBox%A_Index%, %A_LoopField%
   
   Boxes = %A_Index%
}

hGpBox = hp
If Boxes>7
   hGpBox = hp+18
Gui, Add, GroupBox,  xm+128 y0 w195 h35, 
Gui, Add, GroupBox,  xm+335 y0  w264 %hGpBox%, 

; 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

ButtonC:
GuiControl , , SearchBox ,            ; Clear the Searchbox
ControlClick , Dummy , %A_ScriptName%    ; Remove 'clicked' appearance from C button
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:
SearchTerm1=
SearchTerm2=
SearchTerm3=
SearchTerm4=
RecentItems =
Rows=
StringSplit SearchTerm , SearchBox , %A_Space%    ; Space in search term acts as separatorterms

;tooltip, t1=%SearchTerm1%`rt2=%SearchTerm2%`rt3=%SearchTerm3%`rt4=%SearchTerm4%`r

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'
   
   ; Discard if no '.' remains in the filename apart from in .lnk (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
   {
      ;NB. SplitPath incorrectly splits "sometext ....addt" into "sometext ...." and "". 
      ; So SearchBox using Instr() will reject it even if search term is "addt".
      
      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
      }
   }
; Filter using SearchBox contents
   If SearchBox   ; Don't waste time if no search term
   {
         If !InStr(LinkTitle , SearchTerm1)
            Continue
         If !InStr(LinkTitle , SearchTerm2)
            Continue
         If !InStr(LinkTitle , SearchTerm3)
            Continue
         If !InStr(LinkTitle , SearchTerm4)
            Continue
   }   

/*
   If SearchBox
   {
      If SearchTerm1
      {
         If !InStr(LinkTitle , SearchTerm1)
            Continue
      }
      If SearchTerm2
      {
         If !InStr(LinkTitle , SearchTerm2)
            Continue
      }
      If SearchTerm3
      {
         If !InStr(LinkTitle , SearchTerm3)
            Continue
      }
      If SearchTerm4
      {
         If !InStr(LinkTitle , SearchTerm4)
            Continue
      }
   }   
*/

RecentItems = %RecentItems%%A_LoopFileTimeModified%|%A_LoopFileLongPath%`n ; NB 'Time' is when the link was last modified
   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 clicks on a row to activate the selected shortcut.

; When search box is in use date restrictions are removed, and Weeks box greyed out
; to indicate.

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 date restrictions but not if SearchBox in use
   If !SearchBox   
   {   
      GuiControl , Enable , WeeksAgo   ; Unblank the Weeks box
      GuiControl , Enable , Wks   ; Unblank the Weeks box
      If WeeksAgo<99; Apply no date limits if 99 or if SearchBox in use
      {
         ; 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
      }
   }
   Else
   {
      GuiControl , Disable , WeeksAgo   ; Blank the date box to show no date restriction in force
      GuiControl , Disable , Wks   
   }
   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 SearchBox And !RecentItems
   {
      GuiControl , Disable , WeeksAgo   ; Blank the date box to show not in use
      GuiControl , Disable , Wks   
   }

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,MaxBoxes
   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
   
   ; Now apply limit to number of boxes to be used
   TrimmedList =
   Loop , parse , Newlist, `,
   {
      TrimmedList = %TrimmedList%%A_LoopField%`,
      If (A_Index = MaxBoxes)
         Break
   }
   StringTrimRight , TrimmedList , TrimmedList , 1
Return , TrimmedList
}

;---------------------------------------------------------

; 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) Or SearchBox   ; ie no filtering if Weeks is 0 or blank, or search box in use
;If !(WeeksAgo>0)

{
   FilterOn =
   loop, parse , SelectionBoxes , CSV      
   GuiControl , disable , Box%A_Index%
   GuiControl , disable , Incl
   GuiControl , disable , Rej
   GuiControl , disable , All
   GuiControl , disable , Clr
}
Else
{
   FilterOn = 1
   loop, parse , SelectionBoxes , CSV      
   GuiControl , enable , Box%A_Index%
   GuiControl , enable , Incl
   GuiControl , enable , Rej
   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:   
GuiControl , Focus , SearchBox
Incl2:
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
GuiControl , Focus , SearchBox
Gosub , Resize   ; Resize ListView and GUI
Return

; Clears ticks from all the boxes
ButtonClr:
SelectedTypes =
Include = 1
Tick = 0
Gosub , RefreshButtons
GuiControl , Focus , SearchBox
Gosub , Resize   ; Resize ListView and GUI
Return

; Refresh the radio buttons and checkboxes
RefreshButtons:
GuiControl , , Incl , %Include%
GuiControl , , Rej , % 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 --------------
SearchBox:
;OldSearchBox = %SearchBox%
GuiControlGet , SearchBox ,, SearchBox
SearchBox := RegExReplace(SearchBox , "[[:space:]]+" , " " )   ; replace multiple spaces with just one
If SearchBox = %A_Space%
{
   SearchBox =    ; if just a single space, remove it
   GuiControl , , SearchBox ,
   Return
}
StringSplit , Part , SearchBox, %A_Space%
If Part0>=5
{
   ToolTip , Only four separate search terms allowed
   SoundPlay , *48
   ;SearchBox = %OldSearchBox%
   ;GuiControl , , SearchBox , %OldSearchBox%
   SetTimer , StopTooltip , -2000
   Return
}
WeeksFlag =
SetTimer , DoSearchBox , -300 ; Allow time for typing several characters into box
Return

StopTooltip:
ToolTip ,
Return


DoSearchBox:
Gosub , Incl
Return

WeeksAgo:
GuiControlGet , WeeksAgo ,, WeeksAgo
WeeksFlag = 1
SetTimer , DoWeeksAgo , -100
Return

DoWeeksAgo:
Gosub , Incl2
Return

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:
GuiControl , Focus , SearchBox
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. Gives very quick access to recently-used files and folders and presents them in an Explorer-like window with column-sort facilities.

2. To find the recent item that you want, either type in a few keywords to search-as-you-type, or clear the search box and click the buttons to narrow the selection as desired. Once located, you can left or right click on an item to  open it or choose various other options.

3. It uses the contents of Windows' "Recent" file, a set of links, as data source. If a link is no longer valid you get the option to delete it. In case the file was moved, it also searches for the file using Everything.exe (assuming you have this installed).

4. Once launched, you should leave the program running. You can then open or close its display window by left-clicking the Tray icon or by pressing a hotkey.
)

UserInfoMsg =
(
Locating the Item You Want:

1. Type the hotkey or left-click the Tray icon to toggle the display window on and off. There's a reminder of the current hotkey in the bottom left of the main window. Default is Alt-x ('hold down Alt and press x').

2. Finding a file is quicker to do than to explain, but let's assume you want to find a recent doc file with 'Mickey Mouse' in the title. First type Alt-x (or click the Tray icon) to bring up the window, and then carry on typing something like ".d<space>ck<space>mo" (without the quotes). Notice that the Search box has focus as soon as the main window appears, and that groups of characters separated by one or more spaces act as separate search terms. Up to four will work, but usually one or two will find the file you want. When you've finished, click Esc to hide the window, or use Alt-x, click the Tray icon, or click the Close button.

3. As soon as you enter something in the Search box, the other controls grey-out and other filtering is turned off, but if you clear the Search box the other controls come alive. (Tip: The 'c' button clears the box). You can then use the Incl or Rej buttons to Include or Reject the types of files selected by the check-boxes, and to set the depth of search to 1 to 98 weeks back from 'now'. If you set 99 into the 'Wks' box it turns off all time restrictions. If you make it 0 or blank, it selects the past 48 hours and greys out and turns off the other filters.

Example 1: To see only pdf and txt files. Click 'Clr', to clear all boxes and activate 'Include', and then check the pdf and txt boxes.
Example 2: To see all file-types except the ones represented by check-boxes. Click the 'All' button to check all the boxes and activate 'Reject'. After that you can add other types to the mix by checking a box.

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).

When You've Found the Item:

1. Left-click the row to activate the link, or right-click it for more options, pretty much as in Explorer.

2. 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.

3. The Find option searches the computer for the name in the 'File Name' column. For files such as 'ReadMe.txt' it may find many files with the same name but different paths. (Find requires that Everything.exe is installed, see http://www.voidtools.com/).

4. To shut down the program completely, right-click on the Tray icon and select 'Exit'.

Other Information:

1. If you click the column header Type, it sorts into groups of the same type, but within each group the items remain in date order. Click Type again to toggle between alphabetical and reverse alphabetical sorting. Once you've clicked Type, sorting jointly by Type and date continues until you next click the Accessed header when it reverts back to sorting by date only.

2. 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 appears 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 7 boxes can be shown. Both these parameters, MinFrequ and MaxBoxes respectively, can be altered by editing the ini file [See 'Change the Default Settings']. Current values are %MinFrequ% and %MaxBoxes%. With more than 7 boxes the extras form a second row below the first.
)
ChangeHotkey=
(
Changing Hotkey and Other Default Settings:

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 right-click the icon and select 'Change the default settings'. This opens the ini file in NotePad and you can 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. 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%).

3. You can restore the default settings by right-clicking the icon and selecting 'Restore original settings'. Then click OK in the 'second chance' window that pops up.
)

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 --------------------------


Report this post
Top
 Profile  
Reply with quote  
 Post subject: just a great thank's
PostPosted: September 22nd, 2010, 4:03 pm 
Offline

Joined: March 27th, 2008, 7:46 pm
Posts: 129
Location: France
ôh DAT , you are so good for us/me....
Your script is Better my dream...
great continuation..
a fan

_________________
with ahk, all is different!...<img>


Report this post
Top
 Profile  
Reply with quote  
PostPosted: September 23rd, 2010, 5:24 pm 
Offline

Joined: December 1st, 2008, 12:34 pm
Posts: 49
Location: UK
@ soggos - Thanks for the kind remarks.

@ widow - Version 2 has Regex input as an option

It's now moved on quite a lot further so I've started a separate thread for Version 2 at http://www.autohotkey.com/forum/viewtopic.php?t=61620. I think it would have been too confusing to continue it here. The new version also has an Installer.

I hope you like it...

Correction (02-01-2011): The link quoted above for Version 2 is wrong. The correct link is: http://www.autohotkey.com/forum/topic62847.html.


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 7 posts ] 

All times are UTC [ DST ]


Who is online

Users browsing this forum: Google Feedfetcher, rrhuffy and 38 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group