Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

TillaGoto - Go to functions, labels & hks in your script


  • Please log in to reply
135 replies to this topic
HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
Just discovered an error, this is not recognised:
function(){ ;comments
 Return
}

easy fix for each GetScript... functions (I've included in above script as well):

;Get the next function
[color=Red]s:=RegExReplace(s,"\s;.*")[/color]
i := RegExMatch(s, "m)^[[:blank:]]*\K[a-zA-Z0-9#_@\$\?\[\]]+(?=\(.*?\)\s*?\{)", t, i)


TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007

The position is exactly what I wanted but it should not hover the scrollbar.

Yeah, in the next release, I'm going to use system metrics to get the width of the scrollbar so that it opens right beside the scrollbar on any machine.

Here is my version (includes latest changes).
It is attached a little better I think and the rolldown and rollup effect looks cool 8) What do you think :?:

Lol, I like the rolling effect. But I think it's a little too 8). Also, the animation is a little too slow, and also pressing Escape right after pressing the hotkey screws up the animation. But cool nonetheless :)

TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007

easy fix for each GetScript... functions (I've included in above script as well):

;Get the next function
[color=Red]s:=RegExReplace(s,"\s;.*")[/color]
i := RegExMatch(s, "m)^[[:blank:]]*\K[a-zA-Z0-9#_@\$\?\[\]]+(?=\(.*?\)\s*?\{)", t, i)

Thanks for the tip. Your solution is not good though. The script needs to keep the same number of characters because GetLineFromPos relies on that assumption (it uses scintilla api). I'll work on it later (unless you find a fix :)).

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
Labels, comments should work now
Functions, comments should work now
Hotkeys, 1 character hotkeys should work now

Can you check the current roll down and up effect, should be much better now?

EDIT:
Labels comments fix ( [^=\R:]* )
Hotkey fix ( removed [^;] and ; )
/* TheGood	
	TillaGoto - Go to functions and labels in your script
*/

;_________________________________
;CONFIGURATION

uHotkey             := "F1" ;Specify the hotkey you want to use to call up the GUI
iGUIWidth           := 250  ;Specify the width of the GUI
iGUIHeight          := 10   ;Specify, in number of rows, the height of the listbox
iTransparency       := 255  ;Specify the transparency of the GUI. Preferably divisible by 15. Put 255 for no
                            ;transparency at all (consumes less resources). Put 0 to disable fade-in effect.
bQuickMode			:= True 	;Set to True to make TillaGoto go straight to showing the GUI and exit on close.
bMatchEverywhere    := True     ;Set to False for matching to occur only at the beginning of the label/function
                                ;name. Set to True for matching to occur anywhere in the label/function name. As
                                ;well, multiple words can be specified. For example, typing "Dog Cat" will match
                                ;any label/function containing those words anywhere in their name. This is useful
                                ;to search for functions (or labels) only by typing "() FunctionName".
sActiveWindow       := "\.ahk - Notepad\+\+$"   ;Regular expression which should match the window of the editor
                                                ;containing the Scintilla control. Use "ahk - SciTE4AutoHotkey"
                                                ;for SciTE4AutoHotkey
sScintillaClass     := "Scintilla"              ;Class name of the Scintilla control. Exclude instance number.

;______________________________________
;DO NOT CHANGE ANYTHING BELOW THIS LINE

#Include %A_ScriptDir%\RemoteBuf.ahk
#EscapeChar @
#SingleInstance Force
SetTitleMatchMode, RegEx

;Create GUI
Gui, +AlwaysOnTop +Border +ToolWindow +LastFound -Caption
Gui, Font, s8, Courier New
Gui, Margin, 2, 2
Gui, Add, Edit, w%iGUIWidth% h20 vtxtSearch gtxtSearch_Event hwndhtxtsearch,
Gui, Add, ListBox, Sort wp r%iGUIHeight% vlblList glblList_Event hwndhlblList +HScroll,
hGui := WinExist()

;Catch WM_KEYDOWN
OnMessage(256, "GUIKeyDown")

;Check if we're in quick mode
If bQuickMode {
    
	;Check if Notepad++ is active
    hNPP := WinActive(sActiveWindow)
    If Not hNPP
        If !WinActive("AHKToolbar4SciTE") {
            ExitApp
        } else {
            WinActivate,%sActiveWindow%
            hNPP:=WinExist(sActiveWindow)
        }
    If Not hNPP
        ExitApp
    bExitOnClose := True
    Gosub SummonGUI
} Else bExitOnClose := False

;Main monitoring loop
Loop {
	
	Sleep, 200
	
	;Check if Notepad++ is active
	h := WinActive(sActiveWindow)
	
    ;Skip the loop if Notepad++ is not active
	If Not h And Not WinActive("ahk_id " hGui) {
        
        ;Turn off hotkeys
        Hotkey, %uHotkey%, SummonGUI, Off
        
        ;Hide GUI if showing
        If bShowing
            Gosub, GuiEscape
        
		hNPP := 0	;Reset value
		Continue
        
    ;Check if we just found a new window
    } Else If (h <> hNPP) And Not WinActive("ahk_id " hGui) {
        
        ;Remember the newfound Notepad++ window and script
        hNPP := h
        
        ;Turn on hotkeys
        Hotkey, %uHotkey%, SummonGUI, On
	}
}

;------------\
;GUI related |
;------------/

;User summoned the GUI
SummonGUI:
    
    ;Check if we're already showing
    If bShowing
        Goto GuiEscape
    
    ;Get handle to focused control
    ControlGetFocus, cSci, ahk_id %hNPP%
    
    ;Check if it fits the class name
    If InStr(cSci, sScintillaClass)
        ControlGet, hSci, Hwnd,, %cSci%, ahk_id %hNPP%
    
    Gosub, AnalyseScript
    
    ;Check if text is selected
    s := Sci_GetSelText(hSci)
    If (s <> "") And Not InStr(s, "@n") {
        
        ;Copy the selected text in the textbox
        GuiControl,, txtSearch, %s%
        
        ;Create a list based on sel
        CreateList(s)
        
        ;Check if it's just one match. If so, go to it. LB_GETCOUNT.
        SendMessage, 395, 0, 0,, ahk_id %hlblList%
        If (ErrorLevel = 1)
            Goto SelectItem
        
        ;Select all. EM_SETSEL
        SendMessage, 177, 0, -1,, ahk_id %htxtSearch%
        
    } Else {    ;Otherwise, empty the textbox and show the whole list
        GuiControl,, txtSearch,
        CreateList()
    }
    
    
    ;Get window info
    WinGetPos, iX, iY,,, ahk_id %hNPP%
    ControlGetPos, sX, sY, sW, sH, %cSci%, ahk_id %hNPP%
    iX += sX + sW - (iGUIWidth + 2)-18
    iY += sY - 0
   
    Gui, Show, w0 h0
    WinSet, Transparent, 0, ahk_id %hGui%
    Gui, Show, AutoSize x%iX% y%iY%
    bShowing := True
    
    GuiShow()
    ControlFocus,, ahk_id %htxtSearch%
Return

GuiShow(){
	global hGui, tillaH, iTransparency
	If !tillaH
		WinGetPos,,,,tillaH,ahk_id %hGui%
	Gui, Show, h1 NA
	If Not iTransparency Or (iTransparency = 255)    ;Turn off if opaque
        WinSet, Transparent, OFF, ahk_id %hGui%
	else
		WinSet, Transparent, %iTransparency%, ahk_id %hGui%
    Loop % tillaH/10
    {
       Gui, Show,% "NA h" A_Index*10
       Sleep, 10
    }
	Gui, Show,% "NA h" tillaH
}

GuiHide(){
	global hGui,tillaH, iTransparency
    Loop % tillaH/10
    {
       Gui, Show,% "NA h" tillaH-A_Index*10
       Sleep, 10
    }
	Gui, Hide
}

GuiEscape:
    bShowing := False
    Gui, Cancel
    GuiHide()
	If bExitOnClose
		ExitApp
Return

;Incremental searching
txtSearch_Event:
    If bShowing {
        GuiControlGet, s,, txtSearch
        CreateList(s)
    }
Return

lblList_Event:
    If (A_GuiEvent <> "DoubleClick")
        Return
SelectItem:
    
    ;Get selected item index. LB_GETCURSEL
    SendMessage, 0x188, 0, 0,, ahk_id %hlblList%
    s := GetListBoxItem(hlblList, ErrorLevel)
    
    ;Check if it's a label or a function
    StringRight, t, s, 1    
    If (t = ":") {
        Loop %sLabels0% {
            If (sLabels%A_Index% = s) {
                l := sLabels%A_Index%_Line
                Break
            }
        }
    } Else {
        Loop %sFuncs0% {
            If (sFuncs%A_Index% = s) {
                l := sFuncs%A_Index%_Line
                Break
            }
        }
    }
    
    ;Call up the line in the Scintilla control
    ShowLine(l)
    Goto GuiEscape  ;Done
    
Return

GUIKeyDown(wParam, lParam, msg, hwnd) {
    Local iCount
    
    IfEqual wParam, 13, Gosub SelectItem ;Enter
    
    ;Check if it's the textbox
    If (hwnd = htxtSearch) {
	    If (wParam = 38) {
            ControlFocus,, ahk_id %hlblList%
            If Not WrapSel(True)
                SendInput {Up}
        } Else If (wParam = 40) {
            ControlFocus,, ahk_id %hlblList%
            If Not WrapSel(False)
                SendInput {Down}
        }
    } Else If (hwnd = hlblList) {   ;Make up/down wrap around
        If (wParam = 38) Or (wParam = 40)
            Return WrapSel(wParam = 38) ? True : ""
    }
}

WrapSel(bUp) {
    Local iCount, iSel
    
    ;Get selected item index and count. LB_GETCOUNT. LB_GETCURSEL.
    SendMessage, 395, 0, 0,, ahk_id %hlblList%
    iCount := ErrorLevel
    SendMessage, 392, 0, 0,, ahk_id %hlblList%
    iSel := ErrorLevel
    
    ;Select the first/last item. LB_SETCURSEL
    If bUp And (iSel = 0) {
        SendMessage 390, iCount - 1, 0,, ahk_id %hlblList%
        Return 1
    } Else If Not bUp And (iSel = iCount - 1) {
        SendMessage 390, 0, 0,, ahk_id %hlblList%
        Return 1
    }
    Return 0
}

;-------------------\
;Scanning Functions |
;-------------------/

;Retrieves labels and functions of the script
AnalyseScript:
    
    ;Get full text
    sScript := Sci_GetText(hSci)
    ;Prep it
    InvalideBlockComments(sScript)
	
    ;Get labels and functions
    GetScriptLabels(sScript)
    GetScriptHotkeys(sScript)
    GetScriptFunctions(sScript)
    
Return

;This sub analyses the script and add the labels in it to the array
GetScriptLabels(ByRef s) {
    Local i, t
    
    ;Reset counter
	sLabels0 := 0
	i := 1
	Loop {
		
		;Get next label
		i := RegExMatch(s, "m)^[[:blank:]]*\K[a-zA-Z0-9#_@\$\?\[\]]+:[^=\R:]*$", t, i)
		
		;Make sure we found something
		If Not i
			Break
		
		;We found a label. Trim everything after the last colon
		StringLeft t, t, InStr(t, ":", False, 0)
        
		sLabels0 += 1	;Increase counter
		sLabels%sLabels0% := t	;Add to array
        
        ;Get line from pos
   		sLabels%sLabels0%_Line := LineFromPos(i)
        
		;Set i to the beginning of the next line
		i := InStr(s, "@n", False, i) + 1
	}
}

;This sub analyses the script and add the hotkeys in it to the array (uses the same array as labels)
GetScriptHotkeys(ByRef s) {
    Local i, t
    
    i := 1
	Loop {
		
		;Get next hotkey looking thing
        
		i := RegExMatch(s, "m)^[[:blank:]]*\K[[:blank:]a-zA-Z0-9\Q#!^+&<>*~$`-=\[]'/\.,\E]+::", t, i)
		
		;Make sure we found something
		If Not i
			Break
		
		;We found a hotkey.
		sLabels0 += 1	;Increase counter
		sLabels%sLabels0% := t	;Add to array
        
        ;Get line from pos
   		sLabels%sLabels0%_Line := LineFromPos(i)
        
		;Set i to the beginning of the next line
		i := InStr(s, "@n", False, i) + 1
	}
}

;This sub analyses the script and add the functions in it to the array
GetScriptFunctions(ByRef s) {
	Local i, t
    
	;Loop through the functions
    sFuncs0 := 0    ;Prep counter
	i := 1
	Loop {
		
		;Get the next function
		i := RegExMatch(s, "m)^[[:blank:]]*\K[a-zA-Z0-9#_@\$\?\[\]]+(?=\(.*?\)[^\R]*?\{)", t, i)
        
		;Check if we found something
		If (i = 0)
			Break
		
        ;Make sure it's a valid function
        If t Not In If,While
        {   ;Increment counter
    		sFuncs0 += 1
            sFuncs%sFuncs0% := t "()"   ;Add the ()
            sFuncs%sFuncs0%_Line := LineFromPos(i)
        }
        
		;Get the next function
		i := InStr(s, "@n", False, i) + 1
        If (i = 1)
            Break
	}
}

;This function adds a "!" in front of lines inside comment blocks so that they dont interfere
InvalideBlockComments(ByRef s) {
    i := 0, len := StrLen(s)
    
    ;Loop through each block
    Loop {
        
        ;Get next block start
        i := RegExMatch(s, "m)^[[:blank:]]*/\*", "", i + 1)
        
        ;Make sure we got something
        If i {
            
            ;Search for matching */
            j := RegExMatch(s, "m)^[[:blank:]]*\*/", "", i + 1)
            
            ;Check if we got something
            If j {
                
                ;Every char after a LF must be !
                n := i
                
                Loop {
                    n := InStr(s, "@n", False, n+1)
                    If (n > j) Or (Not n)
                        Break
                    NumPut(33, s, n+1, "UChar")
                }
            ;Do it til the end
            } Else {
                
                ;Every char after a LF must be !
                n := i
                
                Loop {
                    n := InStr(s, "@n", False, n+1)
                    If (n = len - 1) Or (Not n)
                        Break
                    NumPut(33, s, n+1, "UChar")
                }
            }
        
        ;We went through all the blocks already
        } Else Break
    }
}

;------------------\
;ListBox Functions |
;------------------/

CreateList(filter = "") {
    Global sLabels0, sFuncs0, bMatchEverywhere, hlblList
    
    ;Clear
    GuiControl,, lblList,|
    
    ;Autotrim
    filter = %filter%
    
    If (filter = "") {  ;Split cases for speed
        Loop %sLabels0%
            GuiControl,, lblList, % sLabels%A_Index%
        Loop %sFuncs0%
            GuiControl,, lblList, % sFuncs%A_Index%
    } Else {
        
        ;Split cases for speed
        If bMatchEverywhere {
            
            ;Parse words
            StringSplit, words, filter, %A_Space%
            
            ;Split cases for speed
            If (words0 > 1) {
                Loop %sLabels0% {
                    bMatch := True
                    i := A_Index
                    Loop %words0% {
                        bMatch := bMatch And InStr(sLabels%i%, words%A_Index%)
                        If Not bMatch
                            Break
                    }
                    If bMatch
                        GuiControl,, lblList, % sLabels%A_Index%
                }
                Loop %sFuncs0% {
                    bMatch := True
                    i := A_Index
                    Loop %words0% {
                        bMatch := bMatch And InStr(sFuncs%i%, words%A_Index%)
                        If Not bMatch
                            Break
                    }
                    If bMatch
                        GuiControl,, lblList, % sFuncs%A_Index%
                }
            ;It's one word
            } Else {
                Loop %sLabels0%
                    If InStr(sLabels%A_Index%, filter)
                        GuiControl,, lblList, % sLabels%A_Index%
                Loop %sFuncs0%
                    If InStr(sFuncs%A_Index%, filter)
                        GuiControl,, lblList, % sFuncs%A_Index%
            }
        } Else {
            Loop %sLabels0%
                If (InStr(sLabels%A_Index%, filter) = 1)
                    GuiControl,, lblList, % sLabels%A_Index%
            Loop %sFuncs0%
                If (InStr(sFuncs%A_Index%, filter) = 1)
                    GuiControl,, lblList, % sFuncs%A_Index%
        }
    }
    
    ;Add hscrollbar if necessary
    ListBoxAdjustHSB(hlblList)
    
    ;Select the first item. LB_SETCURSEL
    SendMessage 390, 0, 0,, ahk_id %hlblList%
}

ListBoxAdjustHSB(hLB) {
   
   ;Declare variables (for clarity's sake)
   dwExtent := 0
   dwMaxExtent := 0
   hDCListBox := 0
   hFontOld := 0
   hFontNew := 0
   VarSetCapacity(lptm, 53)
   
   ;Use GetDC to retrieve handle to the display context for the list box and store it in hDCListBox
   hDCListBox := DllCall("GetDC", "Uint", hLB)

   ;Send the list box a WM_GETFONT message to retrieve the handle to the 
   ;font that the list box is using, and store this handle in hFontNew
   SendMessage 49, 0, 0,, ahk_id %hLB%
   hFontNew := ErrorLevel

   ;Use SelectObject to select the font into the display context.
   ;Retain the return value from the SelectObject call in hFontOld
   hFontOld := DllCall("SelectObject", "Uint", hDCListBox, "Uint", hFontNew)

   ;Call GetTextMetrics to get additional information about the font being used
   ;(eg. to get tmAveCharWidth's value)
   DllCall("GetTextMetrics", "Uint", hDCListBox, "Uint", &lptm)
   tmAveCharWidth := NumGet(lptm, 20)

   ;Get item count using LB_GETCOUNT
   SendMessage 395, 0, 0,, ahk_id %hLB%

   ;Loop through the items
   Loop %ErrorLevel% {

      ;Get list box item text
      s := GetListBoxItem(hLB, A_Index - 1)

      ;For each string, the value of the extent to be used is calculated as follows:
      DllCall("GetTextExtentPoint32", "Uint", hDCListBox, "str", s, "int", StrLen(s), "int64P", nSize)
      dwExtent := (nSize & 0xFFFFFFFF) + tmAveCharWidth

      ;Keep if it's the highest to date
      If (dwExtent > dwMaxExtent)
         dwMaxExtent := dwExtent
      
   }
   
   ;After all the extents have been calculated, select the old font back into hDCListBox and then release it:
   DllCall("SelectObject", "Uint", hDCListBox, "Uint", hFontOld)
   DllCall("ReleaseDC", "Uint", hLB, "Uint", hDCListBox)
   
   ;Adjust the horizontal bar using LB_SETHORIZONTALEXTENT
   SendMessage 404, dwMaxExtent, 0,, ahk_id %hLB%
}

GetListBoxItem(hLB, i) {
      
   ;Get length of item. 394 = LB_GETTEXTLEN
   SendMessage 394, %i%, 0,, ahk_id %hLB%
   
   ;Check for error
   If (ErrorLevel = 0xFFFFFFFF)
      Return ""
   
   ;Prepare variable
   VarSetCapacity(sText, ErrorLevel, 0)
   
   ;Retrieve item. 393 = LB_GETTEXT
   SendMessage 393, %i%, &sText,, ahk_id %hLB%
   
   ;Check for error
   If (ErrorLevel = 0xFFFFFFFF)
      Return ""
   
   ;Done
   Return sText

}

;--------------------\
;Scintilla Functions |
;--------------------/

Sci_GetText(hSci) {
	
	;Used constants
	SCI_GETLENGTH := 2006
	SCI_GETTEXT := 2182

	;Retrieve text length
	SendMessage SCI_GETLENGTH, 0, 0,, ahk_id %hSci%
	iLength := ErrorLevel
	
	;Open remote buffer (add 1 for 0 at the end of the string)
	RemoteBuf_Open(hBuf, hSci, iLength + 1)
	
	;Fill buffer with text
	SendMessage SCI_GETTEXT, iLength + 1, RemoteBuf_Get(hBuf),, ahk_id %hSci%
	
	;Read buffer
	VarSetCapacity(sText, iLength)
	RemoteBuf_Read(hBuf, sText, iLength + 1)
	
	;We're done with the remote buffer
	RemoteBuf_Close(hBuf)
	
	Return sText
}

Sci_GetSelText(hSci) {
    
    ;Used constants
    SCI_GETSELTEXT := 2161
	
	;Get length
	SendMessage SCI_GETSELTEXT, 0, 0,, ahk_id %hSci%
	iLength := ErrorLevel
    
    ;Check if it's none
    If (iLength = 1)
        Return ""
    
    ;Open remote buffer
    RemoteBuf_Open(hBuf, hSci, iLength)
    
    ;Fill buffer
    SendMessage, SCI_GETSELTEXT, 0, RemoteBuf_Get(hBuf),, ahk_id %hSci%
    
    ;Prep var
    VarSetCapacity(sText, iLength)
    RemoteBuf_Read(hBuf, sText, iLength)
    
    ;Done
    RemoteBuf_Close(hBuf)
    
    Return sText
}

LineFromPos(pos) {
    Global
    ;SCI_LINEFROMPOSITION
    SendMessage 2166, pos - 1, 0,, ahk_id %hSci%
    Return ErrorLevel + 1
}

ShowLine(line) {
    Global
    
    ;Get the first visible line
    SendMessage, 2152, 0, 0,, ahk_id %hSci%
    
    If (ErrorLevel < line - 1) {
        
        ;Get the number of lines on screen. SCI_LINESONSCREEN
        SendMessage, 2370, 0, 0,, ahk_id %hSci%
        
        ;Go to the line wanted + lines on screen. SCI_GOTOLINE
        SendMessage, 2024, line - 1 + ErrorLevel, 0,, ahk_id %hSci%
    }
    
    SendMessage, 2024, line - 1, 0,, ahk_id %hSci%
}


HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
Add following to If bQuickMode as clicking onto the toolbar in SciTE4AutoHotkey does not work otherwise:
;Check if we're in quick mode

If bQuickMode {

    

	;Check if Notepad++ is active

    hNPP := WinActive(sActiveWindow)

    If Not hNPP

        If !WinActive("AHKToolbar4SciTE") {

            ExitApp

        } else {

            WinActivate,%sActiveWindow%

            hNPP:=WinExist(sActiveWindow)

        }

    If Not hNPP

        ExitApp

    bExitOnClose := True

    Gosub SummonGUI

} Else bExitOnClose := False


TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007
New release:

- Changed location of GUI: it now shows up right beside the scrollbar
- Fixed script scanning so that line comments don't conflict with scanning (thanks HotKeyIt for reporting it!)
- Fixed bug where single-character hotkeys wouldn't show up (thanks HotKeyIt for reporting it!)
- Fixed possible endless loop if the hotkey is on the last line
- Added mouse wheel support for quick selection (see usage)


Concerning the mouse wheel feature:

Upon opening the GUI (or anytime the TextBox has focus), you can move the selection by using the mouse wheel. After selecting the function you want, simply press the hotkey again to go to the selected item. This is useful for when you see the function in the list. You can then quickly scroll to it.



TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007

Can you check the current roll down and up effect, should be much better now?

No, it still doesn't look good when you press escape quickly after the hotkey.
It sort of rolls back up but then fully shows after.

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
Thanks for changes. Looks to work now.

New wheel function is great. :D

TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007
New release:

- You can now click the middle mouse button for selection using the mouse wheel (using HID instead of mouse hotkey to save memory usage)
- Fixed bug where commented hotkeys would show up in the list



TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007
I just added IMO the greatest feature yet! :D

- Added bUseMButton (see usage and script for details)
- Added EmptyMem() by heresy to keep memory usage to a minimum


About bUseMouse:
bUseMButton := True ;Set to True to enable the middle mouse button as a hotkey to call up the GUI.
                    ;More importantly, this also allows you to go to a function/label simply by
                    ;pressing the middle mouse button on the function/label name.
                    ;Set to False to disable it.

Let me know what you think!

fincs
  • Moderators
  • 1662 posts
  • Last active:
  • Joined: 05 May 2007
Wow... This continues growing and being more and more amazing...
But after studying the code, it does DllCalls to functions that are
only present in Windows XP+, and if you want to have this integrated into
SciTE4AutoHotkey v2 you must use only functions that can be used in Windows 95 (minimum OS) and that don't require additional DLL's.
I really want to integrate this into SciTE4AutoHotkey v2, so please! :(
If you prefer to have a Windows 9x/ME version I'll take care of it and make
the installer install the appropiate version ;)

TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007
The only incompatible APIs I can think of right now are RegisterRawInputDevices and GetRawInputData which require Windows XP minimum. But since the alternative solution (which is to use AutoHotkey's built-in mouse hotkey system, at the expense of more memory usage) also requires at least Windows 2000, I don't know if it's worth it to work up a system to accomodate the user in the specific case they use Windows 2000.

What I can do is to remove features, instead of reworking those features so that they are compatible with Windows 95 (if that's even possible!).
Can you tell me which APIs TillaGoto uses which are not available on Windows 95/98/ME/2000? I can make it check for OS version before engaging in features requiring a minimum version.
The other thing I can do is to make a bare minimum version which is bound to work on all OSes but at the expense of no cool features (like fade-in, mouse hotkeys, HID APIs, etc...).

Also, I released a new version:

- Added iCancelWait (see usage and script for details)

About iCancelWait

If you'd like to close the GUI using the mouse, you can depress the middle mouse button for the time duration specified in iCancelWait (default is 300 ms).



fincs
  • Moderators
  • 1662 posts
  • Last active:
  • Joined: 05 May 2007
I discovered that...
RemoteBuffer (a core unremovable part of TillaGoto) is Windows 2000+ (uses OpenProcess) so I won't be able to provide this tool under Windows 9x/ME :cry:

TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007
That sucks.
At least it's (hopefully) a very small percentage of the population.

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008

There are now 4 additional options
- bEffectOpen + bEffectOpenTime
- bEffectClose + bEffectCloseTime

Enter one of the following options for show and hide GUI effect:

C=CENTER
B=BLEND
S=SLIDE
- For slide effect include SD for slide down or SR to slide from left to right or SU=up SL=slide right to left


Includes also a Fix for SciTE4AutoHotkey toolbar
I had to include SetFormat,integerfast,D in ListBoxAdjustHSB+GetListBoxItem
/* TheGood    
    TillaGoto - Go to functions and labels in your script
*/

;_________________________________
;CONFIGURATION

uHotkey             := "F1"     ;Specify the hotkey you want to use to call up the GUI
bUseMButton         := True     ;Set to True to enable the middle mouse button as a hotkey to call up the GUI.
                                ;More importantly, this also allows you to go to a function/label simply by
                                ;pressing the middle mouse button on the function/label name.
                                ;Set to False to disable it.
iCancelWait         := 300      ;Specify in milliseconds the amount of time the middle mouse button should be
                                ;depressed while the GUI is showing for the GUI to close without selection.
                                ;If the middle mouse button is pressed for any amount shorter than that, the
                                ;selection is validated.
iGUIWidth           := 250      ;Specify the width of the GUI
iGUIHeight          := 15       ;Specify, in number of rows, the height of the listbox
iTransparency       := 255      ;Specify the transparency of the GUI. Preferably divisible by 15. Put 255 for no
                                ;transparency at all (consumes less resources). Put 0 to disable fade-in effect.
bQuickMode          := True    ;Set to True to make TillaGoto go straight to showing the GUI and exit on close.
bPosLeft            := True     ;Position left side instead right

bEffectOpen         = SRD        ;CENTER=C, BLEND=B, SLIDE=S (HOR_POSITIVE=L || HOR_NEGATIVE=R || VER_POSITIVE=D || VER_NEGATIVE=U)
bEffectTimeOpen     := 200		;Time for the effect when showing GUI
bEffectClose        = SLU       ;Same as for EffectOpten, for example SU or C or B
bEffectTimeClose    := 100		;Time for the effect when closing Gui

bMatchEverywhere    := True     ;Set to False for matching to occur only at the beginning of the label/function
                                ;name. Set to True for matching to occur anywhere in the label/function name. As
                                ;well, multiple words can be specified. For example, typing "Dog Cat" will match
                                ;any label/function containing those words anywhere in their name. This is useful
                                ;to search for functions (or labels) only by typing "() FunctionName".
sActiveWindow       := "SciTE4AutoHotkey"   ;Regular expression which should match the window of the editor
                                                ;containing the Scintilla control. Use "ahk - SciTE4AutoHotkey"
                                                ;for SciTE4AutoHotkey
sScintillaClass     := "Scintilla"              ;Class name of the Scintilla control. Exclude instance number.

;______________________________________
;DO NOT CHANGE ANYTHING BELOW THIS LINE

#Include %A_ScriptDir%\RemoteBuf.ahk
#EscapeChar @
#SingleInstance Force
SetTitleMatchMode, RegEx

;Create GUI
Gui, +AlwaysOnTop +Border +ToolWindow +LastFound -Caption
Gui, Font, s8, Courier New
Gui, Margin, 2, 2
Gui, Add, Edit, w%iGUIWidth% h20 vtxtSearch gtxtSearch_Event hwndhtxtsearch,
Gui, Add, ListBox, Sort wp r%iGUIHeight% vlblList glblList_Event hwndhlblList +HScroll,
hGui := WinExist()

;Get scrollbar width
SysGet, SM_CXVSCROLL, 2

;Catch WM_INPUT, WM_KEYDOWN and WM_MOUSEWHEEL
OnMessage(255, "GUIInteract")
OnMessage(256, "GUIInteract")
OnMessage(522, "GUIInteract")

;Register the mouse with RIDEV_INPUTSINK
HID_Register(1, 2, hGui, 0x00000100)

;Check if we're in quick mode
If bQuickMode {
    
   ;Check if Notepad++ is active
    hNPP := WinActive(sActiveWindow)
    If Not hNPP
        If !WinActive("AHKToolbar4SciTE") {
            ExitApp
        } else {
            WinActivate,%sActiveWindow%
            hNPP:=WinExist(sActiveWindow)
        }
    If Not hNPP
        ExitApp
    bExitOnClose := True
    Gosub SummonGUI
} Else bExitOnClose := False

;Main monitoring loop
Loop {
    
    Sleep, 200
    
    ;Check if Notepad++ is active
    h := WinActive(sActiveWindow)
    
    ;Skip the loop if Notepad++ is not active
    If Not h And Not WinActive("ahk_id " hGui) {
        
        ;Turn off hotkeys
        Hotkey, %uHotkey%, SummonGUI, Off
        
        ;Hide GUI if showing
        If bShowing
            Gosub, GuiEscape
        
        hNPP := 0    ;Reset value
        Continue
        
    ;Check if we just found a new window
    } Else If (h <> hNPP) And Not WinActive("ahk_id " hGui) {
        
        ;Remember the newfound Notepad++ window and script
        hNPP := h
        
        ;Turn on hotkeys
        Hotkey, %uHotkey%, SummonGUI, On
    }
}

;------------\
;GUI related |
;------------/

;User summoned the GUI
SummonGUI:

    ;Check if we're already showing
    If bShowing {
        If bWheeled
            Goto SelectItem
        bWheeled := True    ;So that if the user presses the hotkey again,
                            ;the first item selected will be validated
        ControlFocus,, ahk_id %htxtSearch%
        Return
    }
    
    ;Get handle to focused control
    ControlGetFocus, cSci, ahk_id %hNPP%
    
    ;Check if it fits the class name
    If InStr(cSci, sScintillaClass)
        ControlGet, hSci, Hwnd,, %cSci%, ahk_id %hNPP%
    Else Return
    
    Gosub, AnalyseScript
    
    ;Check if we're doing CheckOnClick
    If bCheckClick {
        bCheckClick := False
        If CheckTextClick(clickX, clickY)
            Return
    }
    
    ;Check if text is selected
    s := Sci_GetSelText(hSci)
    If (s <> "") And Not InStr(s, "@n") {
        
        ;Copy the selected text in the textbox
        GuiControl,, txtSearch, %s%
        
        ;Create a list based on sel
        CreateList(s)
        
        ;Check if it's just one match. If so, go to it. LB_GETCOUNT.
        SendMessage, 395, 0, 0,, ahk_id %hlblList%
        If (ErrorLevel = 1)
            Goto SelectItem
        
        ;Select all. EM_SETSEL
        SendMessage, 177, 0, -1,, ahk_id %htxtSearch%
        
    } Else {    ;Otherwise, empty the textbox and show the whole list
        GuiControl,, txtSearch,
        CreateList()
    }
    
    
    ;Get window info
    WinGetPos, iX, iY,,, ahk_id %hNPP%
    ControlGetPos, sX, sY, sW, sH, %cSci%, ahk_id %hNPP%
    iX += sX + sW - (iGUIWidth + 6) - SM_CXVSCROLL
    iY += sY + 2
    
    If bPosLeft
        Gui, Show, Hide AutoSize x5 y%iY%
    else
        Gui, Show, Hide AutoSize x%iX% y%iY%
    bShowing := True
    
	AnimateWindow(hGui,bEffectTimeOpen,"A" . bEffectOpen)
	
    If Not iTransparency Or (iTransparency = 255)    ;Turn off if opaque
        WinSet, Transparent, OFF, ahk_id %hGui%
    Else
        WinSet, Transparent, %iTransparency%, ahk_id %hGui%
    WinActivate, ahk_id %hGui%
    ControlFocus,, ahk_id %htxtSearch%
Return

GuiEscape:
    bShowing := False
    bWheeled := False
    AnimateWindow(hGui,bEffectTimeClose,"H". bEffectClose)
    Gui, Show, w0 h0
    Gui,Cancel
    Gui,Hide
    If bExitOnClose
        ExitApp
    EmptyMem()
Return

;Incremental searching
txtSearch_Event:
    If bShowing {
        GuiControlGet, s,, txtSearch
        CreateList(s)
    }
Return

lblList_Event:
    If (A_GuiEvent <> "DoubleClick")
        Return
SelectItem:
    
    ;Get selected item index. LB_GETCURSEL
    SendMessage, 0x188, 0, 0,, ahk_id %hlblList%
    s := GetListBoxItem(hlblList, ErrorLevel)

    ;Check if it's a label or a function
    StringRight, t, s, 1    
    If (t = ":") {
        Loop %sLabels0% {
            If (sLabels%A_Index% = s) {
                l := sLabels%A_Index%_Line
                Break
            }
        }
    } Else {
        Loop %sFuncs0% {
            If (sFuncs%A_Index% = s) {
                l := sFuncs%A_Index%_Line
                Break
            }
        }
    }
    
    ;Call up the line in the Scintilla control
    ShowLine(l)
    Goto GuiEscape  ;Done
    
Return

AnimateWindow(hwnd,time,options){
    local H:=0x10000, A:=0x20000,C:=0x10, B:= 0x80000,S:=0x40000,R:= 0x1, L:=0x2, D:=0x4, U:=0x8,O:="HACBSLURD",opt:="",Format:=""
    format:=A_FormatInteger
    SetFormat, IntegerFast, Hex
    opt := 0x0 + 0
    Loop,parse,Options
        If InStr(O,A_LoopField)
            opt |= %A_LoopField%
    If opt
        DllCall("AnimateWindow", "UInt", hwnd, "Int", time, "UInt", opt)
    SetFormat, IntegerFast,%A_FormatInteger%
}

GUIInteract(wParam, lParam, msg, hwnd) {
    Local iCount, flags, bDown, bUp, sControl, cX, cY
    Static bIgnoreUp := False
    
    Critical
    
    ;Check which message it is
    If (msg = 256) {    ;WM_KEYDOWN
        
        IfEqual wParam, 13, Gosub SelectItem ;Enter
        
        ;Check if it's the textbox
        If (hwnd = htxtSearch) {
            If (wParam = 38) {
                ControlFocus,, ahk_id %hlblList%
                If Not WrapSel(True)
                    SendInput {Up}
            } Else If (wParam = 40) {
                ControlFocus,, ahk_id %hlblList%
                If Not WrapSel(False)
                    SendInput {Down}
            }
        } Else If (hwnd = hlblList) {   ;Make up/down wrap around
            If (wParam = 38) Or (wParam = 40)
                Return WrapSel(wParam = 38) ? True : ""
        }
    } Else If (msg = 522) And (hwnd = htxtSearch) { ;WM_MOUSEWHEEL
        bWheeled := True
        
        ;Sign it if needed
        wParam := wParam > 0x7FFFFFFF ? -(~wParam) - 1 : wParam

        ;Get notches turned
        wParam := (wParam >> 16) / 120
        
        If (wParam > 0) {
            Loop % Abs(wParam) {    ;%
                If Not WrapSel(True)
                    ControlSend,, {Up}, ahk_id %hlblList%
            }
        } Else {
            Loop % Abs(wParam) {    ;%
                If Not WrapSel(False)
                    ControlSend,, {Down}, ahk_id %hlblList%
            }
        }
    } Else If (msg = 255) { ;WM_INPUT
        
        ;Get flags
        flags := HID_GetInputInfo(lParam, 20 | 0x0100)
        
        ;Check if middle mouse button is down/up
        bDown := flags & 0x0010
        bUp := flags & 0x0020
        
        ;To save time for most cases this branch will be executed
        If Not (bDown Or bUp)
            Return
        
        If bDown And bShowing {
            bIgnoreUp := True
            SetTimer, GuiEscape, -%iCancelWait%
        } Else If bUp And bShowing
            Gosub SelectItem
        Else If bUp And bUseMButton And Not bIgnoreUp And WinActive(sActiveWindow) {
            
            ;Get mouse data
            MouseGetPos, clickX, clickY,, sControl
            If InStr(sControl, sScintillaClass) {
                ControlGet, hSci, Hwnd,, %sControl%, ahk_id %hNPP%
                ControlGetPos, cX, cY,,, %sControl%, ahk_id %hNPP%
                clickX -= cX, clickY -= cY
                
                bCheckClick := True
            }
            
            Critical Off
            Gosub SummonGUI
        }
        
        If bUp And bIgnoreUp
            bIgnoreUp := False
    }
}

WrapSel(bUp) {
    Local iCount, iSel
    
    ;Get selected item index and count. LB_GETCOUNT. LB_GETCURSEL.
    SendMessage, 395, 0, 0,, ahk_id %hlblList%
    iCount := ErrorLevel
    SendMessage, 392, 0, 0,, ahk_id %hlblList%
    iSel := ErrorLevel
    
    ;Select the first/last item. LB_SETCURSEL
    If bUp And (iSel = 0) {
        SendMessage 390, iCount - 1, 0,, ahk_id %hlblList%
        Return 1
    } Else If Not bUp And (iSel = iCount - 1) {
        SendMessage 390, 0, 0,, ahk_id %hlblList%
        Return 1
    }
    Return 0
}

;-------------------\
;Scanning Functions |
;-------------------/

;Retrieves labels and functions of the script
AnalyseScript:
    ;Get full text
    sScript := Sci_GetText(hSci)
    
    ;Prep it
    BlankOutComments(sScript)
    
    ;Get labels and functions
    GetScriptLabels(sScript)
    GetScriptHotkeys(sScript)
    GetScriptFunctions(sScript)
    
Return

;This sub analyses the script and add the labels in it to the array
GetScriptLabels(ByRef s) {
    Local i, t
    
    ;Reset counter
    sLabels0 := 0
    i := 1
    Loop {
        
        ;Get next label
        i := RegExMatch(s, "m)^[[:blank:]]*\K[a-zA-Z0-9#_@\$\?\[\]]+:[[:blank:]]*$", t, i)
        
        ;Make sure we found something
        If Not i
            Break
        
        ;We found a label. Trim everything after the last colon
        StringLeft t, t, InStr(t, ":", False, 0)
        
        sLabels0 += 1    ;Increase counter
        sLabels%sLabels0% := t    ;Add to array
        
        ;Get line from pos
           sLabels%sLabels0%_Line := LineFromPos(i)
        
        ;Set i to the beginning of the next line
        i := InStr(s, "@n", False, i) + 1
        If (i = 1)
            Break
    }
}

;This sub analyses the script and add the hotkeys in it to the array (uses the same array as labels)
GetScriptHotkeys(ByRef s) {
    Local i, t
    
    i := 1
    Loop {
        
        ;Get next hotkey
        i := RegExMatch(s, "m)^[[:blank:]]*\K[[:blank:]a-zA-Z0-9\Q#!^+&<>*~$`-=\[]';/\.,\E]+::", t, i)
        
        ;Make sure we found something
        If Not i
            Break
        
        ;We found a hotkey.
        sLabels0 += 1    ;Increase counter
        sLabels%sLabels0% := t    ;Add to array
        
        ;Get line from pos
           sLabels%sLabels0%_Line := LineFromPos(i)
        
        ;Set i to the beginning of the next line
        i := InStr(s, "@n", False, i) + 1
        If (i = 1)
            Break
    }
}

;This sub analyses the script and add the functions in it to the array
GetScriptFunctions(ByRef s) {
    Local i, t
    
    ;Loop through the functions
    sFuncs0 := 0    ;Prep counter
    i := 1
    Loop {
        
        ;Get the next function
        i := RegExMatch(s, "m)^[[:blank:]]*\K[a-zA-Z0-9#_@\$\?\[\]]+(?=\(.*?\)\s*?\{)", t, i)
        
        ;Check if we found something
        If (i = 0)
            Break
        
        ;Make sure it's a valid function
        If t Not In If,While
        {   ;Increment counter
            sFuncs0 += 1
            sFuncs%sFuncs0% := t "()"   ;Add the ()
            sFuncs%sFuncs0%_Line := LineFromPos(i)
        }
        
        ;Get the next function
        i := InStr(s, "@n", False, i) + 1
        If (i = 1)
            Break
    }
}

BlankOutComments(ByRef s) {
    
    i := 0
    Loop {
        
        ;Get next block
        i := RegExMatch(s, "Psm)^[[:blank:]]*\K/\*.*?\r\s*\*/", len, i + 1)
        
        ;Check if we found anything
        If Not i
            Break
        
        ;Blank out comment
        VarSetCapacity(blank, len, 32)
        DllCall("RtlMoveMemory", UInt, &s + (i - 1), UInt, &blank, UInt, len)
    }
    
    ;Get the comment flag used
    i := RegExMatch(s, "m)^[[:blank:]]*#CommentFlag\K.*?(?=\r)", sCommentFlag)
    If Not i
        sCommentFlag := ";"
    Else sCommentFlag = %sCommentFlag%
    
    ;Check if the very first line is a comment
    If (SubStr(s, 1, StrLen(sCommentFlag)) = sCommentFlag) {
        len := InStr(s, "@r") - 1
        VarSetCapacity(blank, len, 32)
        DllCall("RtlMoveMemory", UInt, &s, UInt, &blank, UInt, len)
    }
    
    i := 0
    Loop {
        
        ;Get next comment
        i := RegExMatch(s, "P)\s+\K\Q" sCommentFlag "\E.*?(?=\r)",  len, i + 1)
        
        ;Check if we found anything
        If Not i
            Break
        
        ;Blank out comment
        VarSetCapacity(blank, len, 32)
        DllCall("RtlMoveMemory", UInt, &s + (i - 1), UInt, &blank, UInt, len)
    }
}

;------------------\
;ListBox Functions |
;------------------/

CreateList(filter = "") {
    Global sLabels0, sFuncs0, bMatchEverywhere, hlblList
    
    ;Clear
    GuiControl,, lblList,|
    
    ;Disable redraw
    GuiControl, -Redraw, lblList
    
    ;Autotrim
    filter = %filter%
    
    If (filter = "") {  ;Split cases for speed
        Loop %sLabels0%
            GuiControl,, lblList, % sLabels%A_Index%
        Loop %sFuncs0%
            GuiControl,, lblList, % sFuncs%A_Index%
    } Else {
        
        ;Split cases for speed
        If bMatchEverywhere {
            
            ;Parse words
            StringSplit, words, filter, %A_Space%
            
            ;Split cases for speed
            If (words0 > 1) {
                Loop %sLabels0% {
                    bMatch := True
                    i := A_Index
                    Loop %words0% {
                        bMatch := bMatch And InStr(sLabels%i%, words%A_Index%)
                        If Not bMatch
                            Break
                    }
                    If bMatch
                        GuiControl,, lblList, % sLabels%A_Index%
                }
                Loop %sFuncs0% {
                    bMatch := True
                    i := A_Index
                    Loop %words0% {
                        bMatch := bMatch And InStr(sFuncs%i%, words%A_Index%)
                        If Not bMatch
                            Break
                    }
                    If bMatch
                        GuiControl,, lblList, % sFuncs%A_Index%
                }
            ;It's one word
            } Else {
                Loop %sLabels0%
                    If InStr(sLabels%A_Index%, filter)
                        GuiControl,, lblList, % sLabels%A_Index%
                Loop %sFuncs0%
                    If InStr(sFuncs%A_Index%, filter)
                        GuiControl,, lblList, % sFuncs%A_Index%
            }
        } Else {
            Loop %sLabels0%
                If (InStr(sLabels%A_Index%, filter) = 1)
                    GuiControl,, lblList, % sLabels%A_Index%
            Loop %sFuncs0%
                If (InStr(sFuncs%A_Index%, filter) = 1)
                    GuiControl,, lblList, % sFuncs%A_Index%
        }
    }
    
    ;Add hscrollbar if necessary
    ListBoxAdjustHSB(hlblList)
    
    ;Select the first item. LB_SETCURSEL
    SendMessage 390, 0, 0,, ahk_id %hlblList%
    
    ;Redraw
    GuiControl, +Redraw, lblList
}

ListBoxAdjustHSB(hLB) {
   SetFormat,IntegerFast,D
   ;Declare variables (for clarity's sake)
   dwExtent := 0
   dwMaxExtent := 0
   hDCListBox := 0
   hFontOld := 0
   hFontNew := 0
   VarSetCapacity(lptm, 53)
   
   ;Use GetDC to retrieve handle to the display context for the list box and store it in hDCListBox
   hDCListBox := DllCall("GetDC", "Uint", hLB)

   ;Send the list box a WM_GETFONT message to retrieve the handle to the 
   ;font that the list box is using, and store this handle in hFontNew
   SendMessage 49, 0, 0,, ahk_id %hLB%
   hFontNew := ErrorLevel

   ;Use SelectObject to select the font into the display context.
   ;Retain the return value from the SelectObject call in hFontOld
   hFontOld := DllCall("SelectObject", "Uint", hDCListBox, "Uint", hFontNew)

   ;Call GetTextMetrics to get additional information about the font being used
   ;(eg. to get tmAveCharWidth's value)
   DllCall("GetTextMetrics", "Uint", hDCListBox, "Uint", &lptm)
   tmAveCharWidth := NumGet(lptm, 20)

   ;Get item count using LB_GETCOUNT
   SendMessage 395, 0, 0,, ahk_id %hLB%

   ;Loop through the items
   Loop %ErrorLevel% {

      ;Get list box item text
      s := GetListBoxItem(hLB, A_Index - 1)

      ;For each string, the value of the extent to be used is calculated as follows:
      DllCall("GetTextExtentPoint32", "Uint", hDCListBox, "str", s, "int", StrLen(s), "int64P", nSize)
      dwExtent := (nSize & 0xFFFFFFFF) + tmAveCharWidth

      ;Keep if it's the highest to date
      If (dwExtent > dwMaxExtent)
         dwMaxExtent := dwExtent
      
   }
   
   ;After all the extents have been calculated, select the old font back into hDCListBox and then release it:
   DllCall("SelectObject", "Uint", hDCListBox, "Uint", hFontOld)
   DllCall("ReleaseDC", "Uint", hLB, "Uint", hDCListBox)
   
   ;Adjust the horizontal bar using LB_SETHORIZONTALEXTENT
   SendMessage 404, dwMaxExtent, 0,, ahk_id %hLB%
}

GetListBoxItem(hLB, i) {
    SetFormat,IntegerFast,D  
   ;Get length of item. 394 = LB_GETTEXTLEN
   SendMessage 394, %i%, 0,, ahk_id %hLB%
   
   ;Check for error
   If (ErrorLevel = 0xFFFFFFFF)
      Return ""
   
   ;Prepare variable
   VarSetCapacity(sText, ErrorLevel, 0)
   
   ;Retrieve item. 393 = LB_GETTEXT
   SendMessage 393, %i%, &sText,, ahk_id %hLB%
   
   ;Check for error
   If (ErrorLevel = 0xFFFFFFFF)
      Return ""
   
   ;Done
   Return sText

}

;--------------------\
;Scintilla Functions |
;--------------------/

Sci_GetText(hSci) {
    
    ;Used constants
    SCI_GETLENGTH := 2006
    SCI_GETTEXT := 2182

    ;Retrieve text length
    SendMessage SCI_GETLENGTH, 0, 0,, ahk_id %hSci%
    iLength := ErrorLevel
    
    ;Open remote buffer (add 1 for 0 at the end of the string)
    RemoteBuf_Open(hBuf, hSci, iLength + 1)
    
    ;Fill buffer with text
    SendMessage SCI_GETTEXT, iLength + 1, RemoteBuf_Get(hBuf),, ahk_id %hSci%
    
    ;Read buffer
    VarSetCapacity(sText, iLength)
    RemoteBuf_Read(hBuf, sText, iLength + 1)
    
    ;We're done with the remote buffer
    RemoteBuf_Close(hBuf)
    
    Return sText
}

Sci_GetSelText(hSci) {
    
    ;Used constants
    SCI_GETSELTEXT := 2161
    
    ;Get length
    SendMessage SCI_GETSELTEXT, 0, 0,, ahk_id %hSci%
    iLength := ErrorLevel
    
    ;Check if it's none
    If (iLength = 1)
        Return ""
    
    ;Open remote buffer
    RemoteBuf_Open(hBuf, hSci, iLength)
    
    ;Fill buffer
    SendMessage, SCI_GETSELTEXT, 0, RemoteBuf_Get(hBuf),, ahk_id %hSci%
    
    ;Prep var
    VarSetCapacity(sText, iLength)
    RemoteBuf_Read(hBuf, sText, iLength)
    
    ;Done
    RemoteBuf_Close(hBuf)
    
    Return sText
}

Sci_GetLineText(hSci, iLine) {
    
    ;Used constants
    SCI_GETLINE := 2153
    SCI_LINELENGTH := 2350
    
    ;Retrieve line length
    SendMessage SCI_LINELENGTH, iLine, 0,, ahk_id %hSci%
    iLength := ErrorLevel
    
    ;Open remote buffer (add 1 for 0 at the end of the string)
    RemoteBuf_Open(hBuf, hSci, iLength + 1)
    
    ;Fill buffer with text
    SendMessage SCI_GETLINE, iLine, RemoteBuf_Get(hBuf),, ahk_id %hSci%
    
    ;Read buffer
    VarSetCapacity(sText, iLength)
    RemoteBuf_Read(hBuf, sText, iLength + 1)
    
    ;We're done with the remote buffer
    RemoteBuf_Close(hBuf)
    
    ;Trim off ending characters
    sText := RegExReplace(sText, "\R")
    
    Return sText
}

LineFromPos(pos) {
    Global
    ;SCI_LINEFROMPOSITION
    SendMessage, 2166, pos - 1, 0,, ahk_id %hSci%
    Return ErrorLevel + 1
}

CheckTextClick(x, y) {
    Local pos, line, linetext, i
    
    ;SCI_POSITIONFROMPOINTCLOSE
    SendMessage, 2023, x, y,, ahk_id %hSci%
    pos := ErrorLevel
    
    ;Check for error
    If (pos = -1)
        Return False
    Else {
        
        ;SCI_LINEFROMPOSITION
        SendMessage, 2166, pos, 0,, ahk_id %hSci%
        line := ErrorLevel
        
        ;SCI_POSITIONFROMLINE
        SendMessage, 2167, line, 0,, ahk_id %hSci%
        pos -= ErrorLevel
        
        ;Get line text
        linetext := Sci_GetLineText(hSci, line)
        
        ;Trim after the first illegal character
        i := RegExMatch(linetext, "[^a-zA-Z0-9#_@\$\?\[\]]", "", pos + 1)
        If i
            StringLeft, linetext, linetext, i - 1
        
        ;Trim before the first illegal character
        i := RegExMatch(StringReverse(linetext), "[^a-zA-Z0-9#_@\$\?\[\]]", "", StrLen(linetext) - pos + 1)
        If i
            StringTrimLeft, linetext, linetext, StrLen(linetext) - i + 1
        
        ;Check if it's a clean match
        linetext .= "()"
        Loop %sFuncs0% {
            If (linetext = sFuncs%A_Index%) {
                ShowLine(sFuncs%A_Index%_Line)
                Return True
            }
        }
        
        StringTrimRight, linetext, linetext, 2
        linetext .= ":"
        Loop %sLabels0% {
            If (linetext = sLabels%A_Index%) {
                ShowLine(sLabels%A_Index%_Line)
                Return True
            }
        }
    }
    
    Return False
}

StringReverse(s) {
    Loop, Parse, s
        ret := A_LoopField ret
    Return ret
}

ShowLine(line) {
    Global
    
    ;Get the first visible line
    SendMessage, 2152, 0, 0,, ahk_id %hSci%
    
    If (ErrorLevel < line - 1) {
        
        ;Get the number of lines on screen. SCI_LINESONSCREEN
        SendMessage, 2370, 0, 0,, ahk_id %hSci%
        
        ;Go to the line wanted + lines on screen. SCI_GOTOLINE
        SendMessage, 2024, line - 1 + ErrorLevel, 0,, ahk_id %hSci%
    }
    
    SendMessage, 2024, line - 1, 0,, ahk_id %hSci%
}

;--------------\
;HID Functions |
;--------------/

HID_Register(UsagePage = False, Usage = False, Handle = False, Flags = 0) {
    RIDEV_REMOVE    := 0x00000001
    RIDEV_EXCLUDE    := 0x00000010
    
    ;Prep var
    VarSetCapacity(uDev, 12)
    
    ;Check if hwnd needs to be null
    bNull := (Flags & RIDEV_REMOVE) Or (Flags & RIDEV_EXCLUDE)
    
    NumPut(UsagePage,            uDev, 0, "UShort")
    NumPut(Usage,                uDev, 2, "UShort")
    NumPut(Flags,                uDev, 4)
    NumPut(bNull ? 0 : Handle,    uDev, 8)
    
    ;Call
    r := DllCall("RegisterRawInputDevices", "UInt", &uDev, "UInt", 1, "UInt", 12)
    
    ;Check for error
    If Not r {
        MsgBox RegisterRawInputDevices call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
        Return -1
    }
    
    Return 0
}

HID_GetInputInfo(InputHandle, Flag) {
    Static uRawInput, iLastHandle    := 0
    RID_INPUT    := 0x10000003
    
    ;Check if it's the same handle
    If (InputHandle = iLastHandle) ;We can retrieve the data without having to call again
        Return NumGet(uRawInput, Flag, HID_NumIsShort(Flag) ? (HID_NumIsSigned(Flag) ? "Short" : "UShort") : (HID_NumIsSigned(Flag) ? "Int" : "UInt"))
    Else {    ;We need to get a fresh copy
        
        ;Get raw data size
        r := DllCall("GetRawInputData", "UInt", InputHandle, "UInt", RID_INPUT, "UInt", 0, "UInt*", iSize, "UInt", 16)
        If (r = -1) Or ErrorLevel {
            MsgBox GetRawInputData call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
            Return -1
        }
        
        ;Prep var
        VarSetCapacity(uRawInput, iSize)
        
        ;Get raw data
        r := DllCall("GetRawInputData", "UInt", InputHandle, "UInt", RID_INPUT, "UInt", &uRawInput, "UInt*", iSize, "UInt", 16)
        If (r = -1) Or ErrorLevel {
            MsgBox GetRawInputData call failed.`nReturn value: %r%`nErrorLevel: %ErrorLevel%`nLine: %A_LineNumber%`nLast Error: %A_LastError%
            ErrorLevel := -1    ;Set errorlevel to fail
            Return -1
        } Else If (r <> iSize) {
            MsgBox GetRawInputData did not return the correct size.`nSize returned: %r%`nSize allocated: %iSize%
            ErrorLevel := -1    ;Set errorlevel to fail
            Return -1
        }
        
        ;Keep handle reference of current uRawInput
        iLastHandle := InputHandle
        
        ;Retrieve data
        Return NumGet(uRawInput, Flag, HID_NumIsShort(Flag) ? (HID_NumIsSigned(Flag) ? "Short" : "UShort") : (HID_NumIsSigned(Flag) ? "Int" : "UInt"))
    }
    
    Return 0
}

;Internal use only
HID_NumIsShort(ByRef Flag) {
    If (Flag & 0x0100) {
        Flag ^= 0x0100    ;Remove it
        Return True
    } Return False
}

;Internal use only
HID_NumIsSigned(ByRef Flag) {
    If (Flag & 0x1000) {
        Flag ^= 0x1000    ;Remove it
        Return True
    } Return False
}

;EmptyMem() by heresy
;http://www.autohotkey.com/forum/viewtopic.php?t=32876
EmptyMem(PID="AHK Rocks"){
    pid:=(pid="AHK Rocks") ? DllCall("GetCurrentProcessId") : pid
    h:=DllCall("OpenProcess", "UInt", 0x001F0FFF, "Int", 0, "Int", pid)
    DllCall("SetProcessWorkingSetSize", "UInt", h, "Int", -1, "Int", -1)
    DllCall("CloseHandle", "Int", h)
}