This script is a barebone wrapper for
Locate32.
It's similar to Rajat's 320mph script, in fact I copied parts of Rajat's code and used some of his ideas, so kudos to him for his script.
When launching a search the script waits for locate to finish before displaying the results. At first I thought it would be slow, but on my machine the results are nearly instantenous, so I didn't implement an sophisticated method to display partial results before the search is finished. YMMV.
I set font size 13, because I watch the monitor from farther than usual. You might have to tweak it for your own taste.
Changes (in reverse chronological order):
- Moved Total Commander path to locate.ahk, so that all the path settings are in one place.
- Added throbber to indicate when the search is in progress.
- Added support for using ndff as a search engine instead of locate.
Note that ndff is much slower than locate, so it's not really suitable as a launcher backend. It was only added to demonstrate that an other backend can be added fairly easily if necessary. For example, if one prefers not using external search tools then a pure AHK search implementation similar to 320mph could be added while keeping the interface provided by the GUI. - Fixed filtering of duplicate files out of search results.
- Directory-constrained searches are done in one step to avoid running locate multiple times. (Thanks majkinetor)
- Results from locate are sorted by access time (decending), so that the most recently accessed files come first, since it's likely the user is searching for one of them.
- When searching then whole pathname is matched, not just the file name, so that any part of the path can be searched. (Thanks Demetris)
- Directory Prefetch is added to the excluded directories. (Thanks Demetris.)
- Added Edit action. An error will occur if the given file has no associated edit action.
- When the selected file is shown in Total Commander the active panel is switched properly.
- Added an API for plugging in different actions without having to modify the main script. The actions are simply needed to be added to locate_actions.ahk.
There are several actions implemented already besides launching the file: copy path to clipboard, insert path to active application, show path in total commander. - Recent choices are shown when the launcher is started and they listed first among the matching entries.
locate.ahk:
Code:
;----------------------------------------------------------------------
; $Id: locate.ahk 21 2007-02-08 18:23:41Z me $
;----------------------------------------------------------------------
;----------------------------------------------------------------------
;
; Type any string to search.
; Select with arrow keys and Enter.
; Cancel with Esc.
; Change pane with Tab.
;
;----------------------------------------------------------------------
MIN_PATTERN_LENGTH = 3
SEARCH_DELAY = 500
MAX_RECENT_CHOICES_STORED = 50
EXCLUDE_DIRECTORIES = Local Settings|Prefetch
FILE_SEARCH_ENGINE = locate ; or: ndff
LOCATE_PATH = %A_ProgramFiles%\Locate\Locate.exe
LOCATE_OPTIONS = -ln:100 -lfd -w
NDFF_PATH = %A_ProgramFiles%\ndff\ndff.exe
NDFF_OPTIONS = -sil -sd -regex
TOTAL_CMD_PATH = %A_ProgramFiles%\totalcmd\TOTALCMD.EXE
GUI_WIDTH = 1100
GUI_HEIGHT = 600
;----------------------------------------------------------------------
SetKeyDelay, 0
;----------------------------------------------------------------------
directory_extension = <DIR>
output_file = %A_Temp%\loc.out
active_field_color = Yellow
passive_field_color = Lime
IniFile = %A_ScriptDir%\locate.ini
IniRead, recent_choices, %IniFile%, Settings, Recent, %A_Space%
pattern =
actions =
lv_width := gui_width - 10
lv_height := gui_height - 10
column_width_name = 300
column_width_ext = 80
column_width_dir := lv_width - column_width_ext - column_width_name - 30
Gui, -Caption +Border +LastFound
gui_handle := WinExist()
Gui, Font, s13, Bitstream Vera Sans Mono
Gui, Add, ListView, x50 y10 w300 h27 -hdr Background%active_field_color% vSelectedSubject readonly, subject
LV_Add("", "")
Gui, Add, ListView, x400 y10 w300 h27 -hdr Background%passive_field_color% vSelectedAction readonly, action
LV_Add("", "")
Gui, Add, Text, x6 y53 w80 h20, Search:
Gui, Add, Slider, x300 y53 vThrobber, 50
GuiControl, Hide, Throbber
Gui, Add, Edit, x95 y49 w150 h28 gSubjectPatternChanged vSubjectPattern
Gui, Add, Edit, x95 y49 w150 h28 gActionPatternChanged vActionPattern
GuiControl, Hide, ActionPattern
Gui, Add, ListView, x6 y90 w%lv_width% h%lv_height% -hdr vActionMatches, Name|Id
LV_ModifyCol(1, lv_width)
LV_ModifyCol(2, 0)
GuiControl, Hide, ActionMatches
Gui, Add, ListView, x6 y90 w%lv_width% h%lv_height% vSubjectMatches, Name|Ext|Directory|Date
LV_ModifyCol(1, column_width_name)
LV_ModifyCol(2, column_width_ext)
LV_ModifyCol(3, column_width_dir)
LV_ModifyCol(4, 0)
now := a_now
Loop, parse, recent_choices, `|
{
add_file_to_listview(a_loopfield, now)
}
GuiControl, Focus, SubjectPattern
Gui, Show, w%GUI_WIDTH% l%GUI_HEIGHT%
select_first_match()
;----------------------------------------------------------------------
#Include %A_ScriptDir%\locate_actions.ahk
;----------------------------------------------------------------------
SubjectPatternChanged:
SetTimer, SearchDelay, %SEARCH_DELAY%
return
;----------------------------------------------------------------------
SearchDelay:
GuiControlGet, text,, SubjectPattern
if (pattern == text)
return
Gui, ListView, SubjectMatches
LV_Delete()
update_subject_selection_display()
pattern := text
if (strlen(text) < MIN_PATTERN_LENGTH)
return
already_found = |
now := a_now
Loop, parse, recent_choices, `|
{
;; not exactly locate syntax, but may be convenient to list these
;; matches first
IfInString, A_LoopField, %pattern%
if (!is_file_found_already(a_loopfield))
add_file_to_listview(a_loopfield, now)
}
GuiControl, , Throbber, 50
GuiControl, Show, Throbber
throbber_increment = 20
settimer, update_throbber, 100
if (FILE_SEARCH_ENGINE == "locate")
{
search_for_pattern(pattern, UserProfile . "\Recent"
. "|" . a_startmenu
. "|" . a_startmenucommon, now)
search_for_pattern(pattern, "")
}
else
search_with_ndff(pattern)
Gui, ListView, SubjectMatches
LV_ModifyCol(4, "SortDesc")
select_first_match()
GuiControl, Hide, Throbber
settimer, update_throbber, off
Return
;----------------------------------------------------------------------
update_throbber:
guicontrolget, pos, , Throbber
if (pos == 0)
throbber_increment = 20
else if (pos == 100)
throbber_increment = -20
pos += throbber_increment
guicontrol, , Throbber, %pos%
return
;----------------------------------------------------------------------
select_first_match()
{
global gui_handle
ControlSend, SysListView324, {Down}, ahk_id %gui_handle%
update_subject_selection_display()
}
;----------------------------------------------------------------------
search_for_pattern(pattern, directories, date = "")
{
global LOCATE_PATH
global LOCATE_OPTIONS
global output_file
options =
if (directories <> "")
{
;; if directory is given then file name match makes more sense, so
;; we override it here in case the user set it in LOCATE_OPTIONS
options := "-lf"
Loop, parse, directories, `|
{
options := options . " -p """ . a_loopfield . """"
}
}
RunWait, %comspec% /c ""%LOCATE_PATH%" %LOCATE_OPTIONS% %options% %pattern% > "%output_file%"",, Hide
process_search_results(date)
}
;----------------------------------------------------------------------
process_search_results(date = "")
{
global EXCLUDE_DIRECTORIES
global output_file
FileRead, Results, %output_file%
if (Results == "")
return
;; remove trailing newline
StringTrimRight, Results, Results, 2
Loop, parse, Results, `n, `r
{
file_name := A_LoopField
skip = 0
Loop, Parse, EXCLUDE_DIRECTORIES, |
{
IfInString, file_name, %A_LoopField%
{
skip = 1
Break
}
}
if (skip == 1)
continue
if (is_file_found_already(file_name))
continue
if date =
FileGetTime, file_date, %file_name%, A
else
file_date := date
add_file_to_listview(file_name, file_date)
}
}
;----------------------------------------------------------------------
search_with_ndff(pattern)
{
global NDFF_PATH
global NDFF_OPTIONS
global output_file
;; adapted from the AHK forum. author: skwire
First_Usable_Drive = 1
DriveGet, Drive_List, List, FIXED
Loop, Parse, Drive_List
{
DriveGet, Drive_Type, FS, %A_LoopField%:
If (Drive_Type == "NTFS")
{
RunWait, %comspec% /c ""%NDFF_PATH%" %NDFF_OPTIONS% -d %A_LoopField%: .*%pattern%.* > "%output_file%"",, Hide
process_search_results()
}
}
}
;----------------------------------------------------------------------
is_file_found_already(file)
{
global already_found
ifinstring, already_found, |%file%|
return true
else
{
already_found = %already_found%%file%|
return false
}
}
;----------------------------------------------------------------------
add_file_to_listview(file_name, date)
{
global directory_extension
SplitPath, file_name, Name, Dir, Ext, NameNoExt
if Ext =
{
FileGetAttrib, Attributes, %file_name%
IfInString, Attributes, D
Ext = %directory_extension%
}
Gui, ListView, SubjectMatches
LV_Add("", NameNoExt, Ext, Dir, date)
}
;----------------------------------------------------------------------
ActionPatternChanged:
GuiControlGet, actionpat,, ActionPattern
Gui, ListView, ActionMatches
LV_Delete()
Loop, parse, actions, `|
{
name := action_names_%a_loopfield%
if actionpat <>
{
IfNotInString, name, %actionpat%
Continue
}
LV_Add("", name, a_loopfield)
}
ControlSend, SysListView323, {Down}, ahk_id %gui_handle%
update_action_selection_display()
return
;----------------------------------------------------------------------
$Enter::
IfWinNotActive, ahk_id %gui_handle%
{
SendInput, {Enter}
Return
}
Gui, Submit
Gui, ListView, ActionMatches
selected := LV_GetNext("Selected")
if (selected == 0)
ExitApp
LV_GetText(action, selected, 2)
sub := action_subs_%action%
Gui, ListView, SubjectMatches
selected := LV_GetNext("Selected")
if (selected == 0)
ExitApp
LV_GetText(Name, selected, 1)
LV_GetText(Ext, selected, 2)
LV_GetText(Dir, selected, 3)
if (Ext == directory_extension)
Ext =
else if (Ext <> "")
Ext := "." . Ext
if (Dir <> "")
Dir := Dir . "\"
SelectedSubject = %Dir%%Name%%Ext%
gosub, %sub%
new_recent_choices := SelectedSubject
Loop, parse, recent_choices, `|
{
if (a_loopfield == SelectedSubject)
continue
new_recent_choices = %new_recent_choices%|%a_loopfield%
if (a_index == MAX_RECENT_CHOICES_STORED)
break
}
IniWrite, %new_recent_choices%, %IniFile%, Settings, Recent
ExitApp
;----------------------------------------------------------------------
$Tab::
IfWinNotActive, ahk_id %gui_handle%
{
SendInput, {Tab}
Return
}
GuiControlGet, visible, Visible, SubjectPattern
if (visible)
{
GuiControl, +Background%passive_field_color%, SelectedSubject
GuiControl, +Background%active_field_color%, SelectedAction
GuiControl, Hide, SubjectPattern
GuiControl, Show, ActionPattern
GuiControl, Hide, SubjectMatches
GuiControl, Show, ActionMatches
GuiControl, Focus, ActionPattern
}
else
{
GuiControl, +Background%passive_field_color%, SelectedAction
GuiControl, +Background%active_field_color%, SelectedSubject
GuiControl, Hide, ActionPattern
GuiControl, Show, SubjectPattern
GuiControl, Hide, ActionMatches
GuiControl, Show, SubjectMatches
GuiControl, Focus, SubjectPattern
}
return
;----------------------------------------------------------------------
$Down::
PassKeyToList("Down")
Return
$Up::
PassKeyToList("Up")
Return
$PgDn::
PassKeyToList("PgDn")
Return
$PgUp::
PassKeyToList("PgUp")
Return
PassKeyToList(key)
{
global gui_handle
IfWinNotActive, ahk_id %gui_handle%
{
SendInput, {%key%}
Return
}
GuiControlGet, visible, Visible, SubjectMatches
if (visible)
{
ControlSend, SysListView324, {%key%}, ahk_id %gui_handle%
update_subject_selection_display()
}
else
{
ControlSend, SysListView323, {%key%}, ahk_id %gui_handle%
update_action_selection_display()
}
}
;----------------------------------------------------------------------
update_subject_selection_display()
{
Gui, ListView, SubjectMatches
selected := LV_GetNext("Selected")
if (selected == 0)
{
Name =
ToolTip
}
else
{
LV_GetText(Name, selected, 1)
LV_GetText(Ext, selected, 2)
LV_GetText(Dir, selected, 3)
ToolTip, %Name% %Ext% %Dir%, 20, -20
}
Gui, ListView, SelectedSubject
LV_Modify(1, "", Name)
}
;----------------------------------------------------------------------
update_action_selection_display()
{
Gui, ListView, ActionMatches
selected := LV_GetNext("Selected")
if (selected == 0)
action =
else
LV_GetText(action, selected, 1)
Gui, ListView, SelectedAction
LV_Modify(1, "", action)
}
;----------------------------------------------------------------------
register_action(name, sub)
{
global
id := name
StringReplace, id, id, %a_space% , _, All
if actions =
actions = %id%
else
actions = %actions%|%id%
action_names_%id% := name
action_subs_%id% := sub
}
;----------------------------------------------------------------------
register_finalize()
{
Gosub, ActionPatternChanged
}
;----------------------------------------------------------------------
GuiEscape:
GuiClose:
ExitApp
;----------------------------------------------------------------------
locate_actions.ahk:
Code:
;----------------------------------------------------------------------
; $Id: locate_actions.ahk 21 2007-02-08 18:23:41Z me $
;----------------------------------------------------------------------
;----------------------------------------------------------------------
; Action plugins for locate.ahk
;
; Do not reorder the sections in this file.
; Follow the instructions when adding new actions.
;----------------------------------------------------------------------
;----------------------------------------------------------------------
; Register actions here.
;
; The action's name and the correspoding subroutine is needed.
;
; When the subroutine is called the variable SelectedSubject contains
; the chosen path. (Don't ask why it's called SelectedSubject.)
;
; The action registered first will be the default.
;----------------------------------------------------------------------
register_action("Launch", "LaunchFile")
register_action("Copy path to clipboard", "CopyToClipboard")
register_action("Edit", "EditFile")
register_action("Insert path into current application", "InsertPath")
register_action("Show file in right Total Commander panel", "ShowFileInTCRight")
register_action("Show file in left Total Commander panel", "ShowFileInTCLeft")
;----------------------------------------------------------------------
; Do not touch this.
;----------------------------------------------------------------------
register_finalize()
;----------------------------------------------------------------------
; Action subroutines.
;----------------------------------------------------------------------
;----------------------------------------------------------------------
; Launch the selected file with the associated application.
; If unsuccessful then open the directory containing the file instead.
;----------------------------------------------------------------------
LaunchFile:
Run, % SelectedSubject,, UseErrorLevel
if ErrorLevel = ERROR
{
SplitPath, SelectedSubject, Name, Dir
Run, % Dir
}
return
;----------------------------------------------------------------------
; Copy the selected path to the clipboard.
;----------------------------------------------------------------------
CopyToClipboard:
clipboard := SelectedSubject
return
;----------------------------------------------------------------------
; Edit file with the associated editor
;----------------------------------------------------------------------
EditFile:
Run, edit %SelectedSubject%
return
;----------------------------------------------------------------------
; Insert the selected path into the current application.
; It's useful, for example, in the Open file dialog box, because
; there is no need to navigate the directories.
;----------------------------------------------------------------------
InsertPath:
Sendraw, % SelectedSubject
return
;----------------------------------------------------------------------
; Show the selected file in right Total Commander panel
;----------------------------------------------------------------------
ShowFileInTCRight:
show_file_in_TC("right")
return
;----------------------------------------------------------------------
; Show the selected file in left Total Commander panel
;----------------------------------------------------------------------
ShowFileInTCLeft:
show_file_in_TC("left")
return
;----------------------------------------------------------------------
; Utility functions
;----------------------------------------------------------------------
show_file_in_TC(panel)
{
global TOTAL_CMD_PATH
global SelectedSubject
SplitPath, SelectedSubject, Name, Dir
if (panel == "right")
switch = R
else
switch = L
Run "%TOTAL_CMD_PATH%" /O /%switch%="%Dir%"
WinWaitActive ahk_class TTOTAL_CMD
active_panel := get_active_tc_panel()
if (active_panel == "right" and panel == "left"
or active_panel == "left" and panel == "right")
Send, {Tab}
; Code is copied from the Total Commander wiki.
PostMessage 1075, 2915 ;call quicksearch box
WinWaitActive ahk_class TQUICKSEARCH
ControlSetText TTabEdit1, %Name%
ControlSend TTabEdit1, {Down}{Esc}
}
;----------------------------------------------------------------------
get_active_tc_panel()
{
WinGetText, text, A
get_panel_file_info(text, lfs1, lfa1, lds1, lda1, rfs1, rfa1, rds1, rda1)
Send, {NumpadMult}
WinGetText, text, A
get_panel_file_info(text, lfs2, lfa2, lds2, lda2, rfs2, rfa2, rds2, rda2)
Send, {NumpadMult}
if (lfs1 <> lfs2 or lds1 <> lds2)
return "left"
if (rfs1 <> rfs2 or rds1 <> rds2)
return "right"
if (lds1 == "")
{
if (rds1 == "")
return get_active_panel_when_both_show_empty_dir()
else
return "left"
}
else
{
if (rds1 == "")
return "right"
else
return get_active_panel_when_both_show_empty_dir()
}
}
;----------------------------------------------------------------------
get_active_panel_when_both_show_empty_dir()
{
Send, {Enter}
WinGetText, text, A
get_panel_file_info(text, lfs2, lfa2, lds2, lda2, rfs2, rfa2, rds2, rda2)
Send, {Enter}
if (lds2 <> "")
return "left"
else
return "right"
}
;----------------------------------------------------------------------
get_panel_file_info(text
, byref left_files_selected
, byref left_files_all
, byref left_dirs_selected
, byref left_dirs_all
, byref right_files_selected
, byref right_files_all
, byref right_dirs_selected
, byref right_dirs_all)
{
left_files_selected =
Loop, parse, text, `n, `r
{
if (regexmatch(a_loopfield, "([0-9]+) / ([0-9]+) files(, ([0-9]+) / ([0-9]+) dir)?", match))
{
if left_files_selected =
{
left_files_selected := match1
left_files_all := match2
left_dirs_selected := match4
left_dirs_all := match5
}
else
{
right_files_selected := match1
right_files_all := match2
right_dirs_selected := match4
right_dirs_all := match5
}
}
}
}