Text Filter

Post your working scripts, libraries and tools for AHK v1.1 and older
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: Text Filter

Post by SundayProgrammer » 15 Jan 2021, 21:03

a little overhaul (inside the codes), kinda.

accommodated the goodies all along while having the multi-level thing kept.

also enabled mouse wheel for the find next match, in which, i devised a donkey way to have the reversed direction implemented. it works fine if there are not tons of matches in the result. otherwise, the {wheelup} will be a bit sluggish. (hoping someone can provide a better method for it.)
for the mouse wheel to work, you have to move your mouse pointer away (out of the "control" namely "Edit2", i.e. the big text box) while the gui is active. otherwise, the mouse wheel will do only the usual operation i.e. scroll the stuff.

still, some more cosmetic touches for this version.

wish it works all well.

Code: Select all

Haystack0=
(
The Road Not Taken
BY ROBERT FROST
Two roads diverged in a yellow wood,
And sorry I could not travel both
And be one traveler, long I stood
And looked down one as far as I could
To where it bent in the undergrowth;
Then took the other, as just as fair,
And having perhaps the better claim,
Because it was grassy and wanted wear;
Though as for that the passing there
Had worn them really about the same,
And both that morning equally lay
In leaves no step had trodden black.
Oh, I kept the first for another day!
Yet knowing how way leads on to way,
I doubted if I should ever come back.
I shall be telling this with a sigh
Somewhere ages and ages hence:
Two roads diverged in a wood, and I—
I took the one less traveled by,
And that has made all the difference.
)
n := 1, pn := n - 1, Haystack%n% := Haystack%pn%
gui, font, s10
gui, add, text,, `t`tOptions:
gui, add, checkbox, ym vfayt checked tabstop gfaythdlr, Instant Filter (aka Find As You Type)
gui, add, checkbox, ym vcase checked tabstop goptshdlr, Case Insensitive
gui, add, checkbox, ym vwhole tabstop goptshdlr, Whole Word
gui, font, s18, arial new
gui, add, text, xm section, Filter:
w := a_screenwidth - 257
gui, add, edit, w%w% ys gspathdlr
gui, add, button, h38 ys default, Go
Gui, Add, Button, h38 ys, Back
h := a_screenheight - 143, w := a_screenwidth - 30
gui, add, edit, readonly x10 h%h% w%w% hscroll vscroll +hwndhEdit
gui, show,, The Text Filter,, The Text Filter - Level %n%   ( Base %blc% Lines )
skipwork = 0
renewGui:
guicontrol,, Edit2, % Haystack%n%
controlfocus, Edit1, A
setwintitle:
strreplace(Haystack%n%,"`n",,blc), blc += strlen(Haystack%n%) and substr(Haystack%n%,0) != "`n" ? 1 : 0
winsettitle, The Text Filter,, The Text Filter - Level %n%   ( Base %blc% Lines )
return
faythdlr:
	if fayt()
		if (Haystack%n% != Haystack%pn%)
		{	Needle%n% := spat()
			theHaystack := Haystack%n%, theNeedle := Needle%n%
			n += 1, pn := n - 1
			Haystack%n% := theHaystack, Needle%n% := theNeedle
			gosub, setwintitle
		}
optshdlr:
	controlfocus, Edit1, A
	goto, spathdlr
buttongo:
	controlfocus, Edit1, A
	progress, zh0 w380 c10 fs18, `nCommitting This Level`n,, Prompt, Segoe UI
	commitlevel = 1
spathdlr:
	if skipwork
	{	skipwork = 0
		return
	}else if not fayt() and not commitlevel
		return
	gui, submit, nohide
	Needle%n% := spat()
	if not strlen(Needle%n%) and commitlevel
	{	commitlevel = 0
		goto, buttonback
	}
	r := (case ? "i" : "") "m`a)^(?!"
	loop, parse, Needle%n%, % a_space
		if substr(a_loopfield,1,1) = "-"
			r .= "(?!.*" substr(a_loopfield,2) ")"
		else r .= "(?=.*" (whole ? "\b" : "") a_loopfield (whole ? "\b" : "") ")"
	r .= ").*\R?"
	theHaystack := regexreplace(Haystack%pn%,r)
	if commitlevel
	{	if (theHaystack = Haystack%pn%)
		{	progress, off
			msgbox, Result No Change Thus Not Anew Level
		}else
		{	if not fayt()
				Haystack%n% := theHaystack
			n += 1, pn := n - 1
			Haystack%n% := theHaystack
			Send, ^a
		}
		commitlevel = 0
		sleep, 500
		progress, off
	}else Haystack%n% := theHaystack
	goto, renewGui
buttonback:
	controlfocus, Edit1, A
	Needle%n% := spat()
	if (n = 1) and (Haystack1 != Haystack0)
		Haystack1 := Haystack0, Needle1 := ""
	else if (Haystack%n% = Haystack%pn%) and (Needle%n% = Needle%pn%)
		if n > 2
			n -= 2, pn := n - 1
		else if n = 2
			n := 1, pn := n - 1, Haystack1 := Haystack0, Needle1 := ""
		else n = 0
	else n -= 1, pn := n - 1
	if n
	{	skipwork = 1
		gosub, renewGui
		guicontrol,, Edit1, % Needle%n%
		send, ^a
		if (n = 1) and (Haystack1 = Haystack0)
		{	progress, zh0 w380 c10 fs18, `nReturned To The Origin`n,, Prompt, Segoe UI
			sleep, 500
			progress, off
		}else
		{	progress, zh0 w380 c10 fs18, `nReturned To Level %n%`n,, Prompt, Segoe UI
			sleep, 500
			progress, off
		}
		return
	}else exitapp
guiescape:
guiclose:
	exitapp

spat() {
	guicontrolget, spat,, Edit1
	return spat
}
fayt() {
	guicontrolget, fayt,, Button1
	return fayt
}
~Bs::
~Del::
	if fayt()
		goto, spathdlr
	else return

focusedcontrol() {
	controlgetfocus, foco
	return foco
}
#if, winactive("The Text Filter") and focusedcontrol() = "Edit1"
	`::
	tab::controlfocus, Edit2, A
	~up::wheelup
	~down::wheeldown
	~left::wheelleft
	~right::wheelright
#if, winactive("The Text Filter") and focusedcontrol() = "Edit2"
	Tab::ControlFocus, Edit1, A
	~space::pgdn
	~+space::pgup
	~s::home
	~e::end
	~t::^home
	~b::^end
	`::
	~enter::
		controlget, cln, currentline,, Edit2, A
		hays := strsplit(Haystack%n%,"`n","`r")
		clipboard := hays[cln]
		msgbox,,, Has Put This Line Into Clipboard, 3
		exitapp
	~f::
		nextmatch:
		controlgettext, sPat, Edit1, A
		if sPat
		{	r := case ? "i)" : ""
			loop, parse, sPat, % a_space
				if substr(a_loopfield,1,1) != "-"
					r .= (whole ? "\b" : "") a_loopfield (whole ? "\b" : "") "|"
		}else
		{	controlget, sPat, selected,, Edit2, A
			r := sPat ? (case ? "i)" : "") (whole ? "\b" : "") sPat (whole ? "\b" : "") : ""
		}
		if r
		{	if substr(r,0) = "|"
				r := substr(r,1,strlen(r)-1)
			controlgettext, hays, Edit2, A
			if regexmatch(hays,r)
			{	starthere:
				if a_thishotkey = ~wheelup
				{	gosub, compute_cp
					gosub, reverseway
					mpos -= 1
				}else
				{	controlget, slctd, selected,, Edit2, A
					if slctd
						send, {left}{right}
					gosub, compute_cp
					mpos := regexmatch(hays,r,nm,cp) - 1
				}
				if mpos >= 0
				{	cp := mpos + strlen(nm)
					sendmessage, 0xb1, mpos, cp,, ahk_id %hEdit%	;EM_SETSEL
					sendmessage, 0xb7, 0, 0,, ahk_id %hEdit%	;EM_SCROLLCARET
				}else if (mpos < 0) and a_thishotkey != ~wheelup
				{	send, ^{home}
					goto, starthere
				}
			}else msgbox, Target Not Found
		}
		return
		compute_cp:
			controlget, cln, currentline,, Edit2, A
			controlget, cco, currentcol,, Edit2, A
			cp := cln > 1 ? instr(hays,"`r`n",,,cln-1) + 1 + cco : cco
			return
		reverseway:
			if cp > 1
			{	mpos := 0, pmax := cp - 1
				loop
				{	npos := regexmatch(hays,r,pm,mpos+1)
					if npos between 1 and %pmax%
						mpos := npos, nm := pm
					else break
				}
				if (npos > pmax) and mpos > 0
					return
			}
			mpos := cp - 1, pmax := strlen(hays)
			loop
			{	npos := regexmatch(hays,r,pm,mpos+1)
				if npos between %cp% and %pmax%
					mpos := npos, nm := pm
				else break
			}
			return
#ifwinactive, The Text Filter
	~wheelup::
	~wheeldown::
		mousegetpos,,,, moc
		if (moc != "Edit2")
		{	controlfocus, Edit2, A
			goto, nextmatch
		}else return
#ifwinactive
fixed a minor bug.
some minor adjustments.
fixed a minor bug: added "controlfocus, Edit1, A' for buttongo and buttonback.
fixed a minor bug: changed from blc += substr(Haystack%n%,0) = "`n" ? 0 : 1 to blc += strlen(Haystack%n%) and substr(Haystack%n%,0) != "`n" ? 1 : 0
Last edited by SundayProgrammer on 19 Jan 2021, 04:19, edited 5 times in total.
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: Text Filter

Post by SundayProgrammer » 15 Jan 2021, 21:28

rommmcek wrote:
15 Jan 2021, 14:51
I'm not an expert! But Notepad++ is using Scintilla control, This thread is using standard Windows Edit control. Your wish is like: Can MS Notepad color all matches? The Answer is no!
However e.g. AutoGUI uses Scintilla too and there it is possible. If Text Filter will ever implement Scintilla... I don't know, but you can start it by yourself and I'm sure you'll get some help form members...
i hope somebody can make it happened, or give more hints how to accomplish it (as long as it will not be too much effort for a mini tool like this one.)
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: Text Filter

Post by SundayProgrammer » 16 Jan 2021, 01:05

fixed a minor bug.
ozzii
Posts: 481
Joined: 30 Oct 2013, 06:04

Re: Text Filter

Post by ozzii » 16 Jan 2021, 04:54

SundayProgrammer wrote:
15 Jan 2021, 21:28
i hope somebody can make it happened, or give more hints how to accomplish it (as long as it will not be too much effort for a mini tool like this one.)
Maybe to change the edit box with and HTMLbox. So you can do what you want like in HTML.
But I don't know if the live search will work. Above my pay grade :headwall:
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: Text Filter

Post by SundayProgrammer » 21 Jan 2021, 09:27

added two options. they are, the line number and the word wrap.

changed the method for "compute_cp", and hence, it works fine regardless of word wrap or not.

line numbering may take a few seconds if the text is rather big.

if you empty the "edit1", you may find next match for whatever target highlighted by yourself. otherwise, find next match will always base on what's being entered in "edit1".

empty "edit1" and hit {enter} (or click "buttongo") will bring you one level back (that is, equivalent to clicking "buttonback".)

that's it for now. and hope it works all well.

Code: Select all

Haystack=
(
The Road Not Taken
BY ROBERT FROST
Two roads diverged in a yellow wood,
And sorry I could not travel both
And be one traveler, long I stood
And looked down one as far as I could
To where it bent in the undergrowth;
Then took the other, as just as fair,
And having perhaps the better claim,
Because it was grassy and wanted wear;
Though as for that the passing there
Had worn them really about the same,
And both that morning equally lay
In leaves no step had trodden black.
Oh, I kept the first for another day!
Yet knowing how way leads on to way,
I doubted if I should ever come back.
I shall be telling this with a sigh
Somewhere ages and ages hence:
Two roads diverged in a wood, and I—
I took the one less traveled by,
And that has made all the difference.
)
n := 1, pn := n - 1, Haystack%n% := Haystack%pn% := Haystack
regFayt := regCase := 1, regWhole := regLinum := regWwrap := 0
theGui:
Gui, tf:New
Gui, Font, s10, Arial New
Gui, Add, Text,, O p t i o n s:
begFayt := regFayt ? "Checked" : ""
Gui, Add, Checkbox, ym %begFayt% Tabstop gtfFaytHdlr, Instant Filter (aka Find As You Type)
begCase := regCase ? "Checked" : ""
Gui, Add, Checkbox, ym %begCase% Tabstop gtfOptsHdlr, Case Insensitive
begWhole := regWhole ? "Checked" : ""
Gui, Add, Checkbox, ym %begWhole% Tabstop gtfOptsHdlr, Whole Word
begLinum := regLinum ? "Checked" : ""
Gui, Add, Checkbox, ym %begLinum% Tabstop gtfLineHdlr, Line Number
beg_Wrap := regWwrap ? "Checked" : ""
Gui, Add, Checkbox, ym %beg_Wrap% Tabstop gtfWrapHdlr, Word Wrap
Gui, Font, s18, Arial New
Gui, Add, Text, xm Section, Filter:
w := A_ScreenWidth - 257
Gui, Add, Edit, w%w% ys gtfFpatHdlr
Gui, Add, Button, h38 ys Default, Go
Gui, Add, Button, h38 ys, Back
h := A_ScreenHeight - 143, w := A_ScreenWidth - 30
begWwrap := regWwrap ? "" : "HScroll"
Gui, Font, s18 Bold, Courier New
Gui, Add, Edit, ReadOnly x10 h%h% w%w% %begWwrap% VScroll +hwndhEdit
Gui, Show,, The Text Filter,, The Text Filter - Level %n%   ( Base %blc% Lines )
ControlGet, cid, hwnd,, Edit2, A
SkipWork = 0
RenewGui:
GuiControl,, Edit2, % Haystack%n%
ControlFocus, Edit1, A
SetWinTitle:
StrReplace(Haystack%n%, "`n",, blc), blc += StrLen(Haystack%n%) and SubStr(Haystack%n%, 0) != "`n" ? 1 : 0
WinSetTitle, The Text Filter,, The Text Filter - Level %n%   ( Base %blc% Lines )
Return
tfFaytHdlr:
	regFayt := Fayt()
	If regFayt
		If (Haystack%n% != Haystack%pn%)
		{	Needle%n% := Fpat()
			theHaystack := Haystack%n%, theNeedle := Needle%n%
			n += 1, pn := n - 1
			Haystack%n% := theHaystack, Needle%n% := theNeedle
			Gosub, SetWinTitle
		}
tfOptsHdlr:
	ControlFocus, Edit1, A
	Goto, tfFpatHdlr
tfLineHdlr:
	If regLinum := Linum()
	{	Progress, zh0 w300 c10 fs18, %rn%Hang On...%rn%,, Working On Line Numbering, Segoe UI
		StrReplace(Haystack, "`n",, oc), oc += StrLen(Haystack) and SubStr(Haystack, 0) != "`n" ? 1 : 0, lsl := StrLen(oc)
		HaystackN =
		Loop, Parse, Haystack, `n
			HaystackN .= SubStr(StrRepeat(A_Space, lsl) A_Index, 1-lsl) ":" A_Space A_LoopField "`n"
		Progress, Off
		Haystack0 := HaystackN
	}Else Haystack0 := Haystack
	Loop, % n
	{	nn := A_Index, pn := nn - 1
		Haystack%nn% := RegexReplace(Haystack%pn%, Maker(Needle%nn%))
	}
	Goto, RenewGui
tfWrapHdlr:
	regWwrap := Wwrap()
	Needle%n% := Fpat()
	Gui, tf:Destroy
	Gosub, theGui
	SkipWork = 1
	GuiControl,, Edit1, % Needle%n%
	Send, {End}
	Return
tfButtonGo:
	ControlFocus, Edit1, A
	Progress, zh0 w380 c10 fs18, `nCommitting This Level`n,, Prompt, Segoe UI
	CommitLevel = 1
tfFpatHdlr:
	If SkipWork
	{	SkipWork = 0
		Return
	}Else If not regFayt and not CommitLevel
		Return
	Gui, Submit, NoHide
	regCase := Case(), regWhole := Whole()
	Needle%n% := Fpat()
	If not StrLen(Needle%n%) and CommitLevel
	{	CommitLevel = 0
		Goto, tfButtonBack
	}
	theHaystack := RegexReplace(Haystack%pn%, Maker(Needle%n%))
	If CommitLevel
	{	If (theHaystack = Haystack%pn%)
		{	Progress, Off
			MsgBox, Result No Change Thus Not Anew Level
		}Else
		{	If not regFayt
				Haystack%n% := theHaystack
			If (blc < 2) and not (Needle%n% == Needle%pn%)
				Progress, zh0 w450 c10 fs18, `nUltimate/Empty Result Reached`nThus Level Not Advanced`n,, Prompt, Segoe UI
			Else
			{	n += 1, pn := n - 1
				Haystack%n% := theHaystack
				Send, ^a
			}
		}
		CommitLevel = 0
		Sleep, 500
		Progress, Off
	}Else Haystack%n% := theHaystack
	Goto, RenewGui
	Maker(t) {
		r := (Case() ? "i" : "") "m`a)^(?!"
		Loop, Parse, t, % A_Space
			If SubStr(A_LoopField, 1, 1) = "-"
				r .= "(?!.*" SubStr(A_LoopField, 2) ")"
			Else r .= "(?=.*" (Whole() ? "\b" : "") A_LoopField (Whole() ? "\b" : "") ")"
		r .= ").*\R?"
		Return r
	}
tfButtonBack:
	ControlFocus, Edit1, A
	Needle%n% := Fpat()
	If (n = 1) and (Haystack1 != Haystack0)
		Haystack1 := Haystack0, Needle1 := ""
	Else If (Haystack%n% = Haystack%pn%) and (Needle%n% = Needle%pn%)
		If n > 2
			n -= 2, pn := n - 1
		Else If n = 2
			n := 1, pn := n - 1, Haystack1 := Haystack0, Needle1 := ""
		Else n = 0
	Else n -= 1, pn := n - 1
	If n
	{	Gosub, RenewGui
		SkipWork = 1
		GuiControl,, Edit1, % Needle%n%
		Send, ^a
		If (n = 1) and (Haystack1 = Haystack0)
		{	Progress, zh0 w380 c10 fs18, `nReturned To The Origin`n,, Prompt, Segoe UI
			Sleep, 500
			Progress, Off
		}Else
		{	Progress, zh0 w380 c10 fs18, `nReturned To Level %n%`n,, Prompt, Segoe UI
			Sleep, 500
			Progress, Off
		}
		Return
	}Else Goto, tfGuiClose
tfGuiEscape:
tfGuiClose:
	ExitApp

Fpat() {
	GuiControlGet, Fpat,, Edit1
	Return Fpat
}
Fayt() {
	GuiControlGet, Fayt,, Button1
	Return Fayt
}
Case() {
	GuiControlGet, Case,, Button2
	Return Case
}
Whole() {
	GuiControlGet, Whole,, Button3
	Return Whole
}
Linum() {
	GuiControlGet, Linum,, Button4
	Return Linum
}
Wwrap() {
	GuiControlGet, Wwrap,, Button5
	Return Wwrap
}
~Bs::
~Del::
	If regFayt
		Goto, tfFpatHdlr
	Else Return

StrRepeat(string, times) {
	Loop, %times%
		output .= string
	Return output
}

FocusedControl() {
	ControlGetFocus, foco
	Return foco
}
#If, WinActive("The Text Filter") and FocusedControl() = "Edit1"
	`::
	Tab::ControlFocus, Edit2, A
	~Up::WheelUp
	~Down::WheelDown
	~Left::WheelLeft
	~Right::WheelRight
#If, WinActive("The Text Filter") and FocusedControl() = "Edit2"
	Tab::ControlFocus, Edit1, A
	~Space::PgDn
	~+Space::PgUp
	~s::Home
	~e::End
	~t::^Home
	~b::^End
	`::
	~Enter::
		ControlGet, cln, CurrentLine,, Edit2, A
		Hays := StrSplit(Haystack%n%, "`n", "`r")
		Clipboard := Hays[cln]
		MsgBox,,, Has Put This Line Into Clipboard, 3
		Goto, tfGuiClose
	~f::
		NextMatch:
		ControlGettext, sPat, Edit1, A
		If StrLen(sPat)
		{	r := regCase ? "i)" : ""
			Loop, Parse, sPat, % A_Space
				If SubStr(A_LoopField, 1, 1) != "-"
					r .= (regWhole ? "\b" : "") A_LoopField (regWhole ? "\b" : "") "|"
		}Else
		{	ControlGet, sPat, Selected,, Edit2, A
			r := StrLen(sPat) ? (regCase ? "i)" : "") (regWhole ? "\b" : "") sPat (regWhole ? "\b" : "") : ""
		}
		If StrLen(r)
		{	If SubStr(r, 0) = "|"
				r := SubStr(r, 1, StrLen(r)-1)
			ControlGettext, Hays, Edit2, A
			If RegExMatch(Hays, r)
			{	StartHere:
				Gosub, Compute_cp
				If A_ThisHotkey = ~Wheelup
				{	Gosub, ReverseWay
					mpos -= 1
				}Else mpos := RegExMatch(Hays, r, nm, cp) - 1
				If mpos >= 0
				{	cp := mpos + StrLen(nm)
					SendMessage, 0xB1, mpos, cp,, ahk_id %hEdit%	;EM_SETSEL
					SendMessage, 0xB7, 0, 0,, ahk_id %hEdit%	;EM_SCROLLCARET
				}Else If (mpos < 0) and A_ThisHotkey != ~Wheelup
				{	Send, ^{Home}
					Goto, StartHere
				}
			}Else MsgBox, Target Not Found
		}
		Return
		Compute_cp:
			DllCall("User32\SendMessage", "Ptr", cid, "UInt", 0x00B0, "UIntP", slcs, "UIntP", slce, "Ptr"), slcs++, slce++
			cp := (A_ThisHotkey = "~WheelUp") ? slcs : slce
			Return
		ReverseWay:
			If cp > 1
			{	mpos := 0, pmax := cp - 1
				Loop
				{	npos := RegExMatch(Hays, r, pm, mpos+1)
					If npos between 1 and %pmax%
						mpos := npos, nm := pm
					Else Break
				}
				If (npos > pmax) and mpos > 0
					Return
			}
			mpos := cp - 1, pmax := StrLen(Hays)
			Loop
			{	npos := RegExMatch(Hays, r, pm, mpos+1)
				If npos between %cp% and %pmax%
					mpos := npos, nm := pm
				Else Break
			}
			Return
#IfWinActive, The Text Filter
	~Wheelup::
	~Wheeldown::
		MouseGetPos,,,, moc
		If (moc != "Edit2")
		{	ControlFocus, Edit2, A
			Goto, NextMatch
		}Else Return
#IfWinActive
User avatar
rommmcek
Posts: 1470
Joined: 15 Aug 2014, 15:18

Re: Text Filter

Post by rommmcek » 24 Jan 2021, 10:27

Maybe you should limit Line numbering based on the length of the text.
Work around for wrap is rather "back to the roots of your code". It would be better to use something like:
Spoiler
But native Edit wrap is not flexible. I made my own attempt (not perfect though):
Spoiler
I wrote ; RRR comments where I made changes, but in aftermath, hope I didn't miss some...

P.s.: I'm working (very slowly) on... see pic
Attachments
Text Filter Sci 1.1.zip
(546.9 KiB) Downloaded 70 times
Sci Doc Filt.png
Sci Doc Filt.png (198.67 KiB) Viewed 2211 times
Last edited by rommmcek on 22 Mar 2021, 14:38, edited 4 times in total.
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: Text Filter

Post by SundayProgrammer » 24 Jan 2021, 19:22

rommmcek wrote:
24 Jan 2021, 10:27
Maybe you should limit Line numbering based on the length of the text.
yes, better do that.
rommmcek wrote:
24 Jan 2021, 10:27
Work around for wrap is rather "back to the roots of your code". It would be better to use something like:
Spoiler
yes, i knew this approach from a quick research when i hit the wall. i didn't go up that path however mostly because of my laziness (i can overcome it sometimes somehow, not very often though.)
rommmcek wrote:
24 Jan 2021, 10:27
But native Edit wrap is not flexible. I made my own attempt (not perfect though):
Spoiler
nice attempt, but it seems doesn't go well with different text box size (width). btw, the "extended parameters" thing is very interesting. eyes opened.
rommmcek wrote:
24 Jan 2021, 10:27
I wrote ; RRR comments where I made changes, but in aftermath, hope I didn't miss some...

P.s.: I'm working (very slowly) on... see pic
wow, that's a leap, i'm looking forward to its launch. :clap:

@rommmcek, THANK YOU.
User avatar
rommmcek
Posts: 1470
Joined: 15 Aug 2014, 15:18

Re: Text Filter

Post by rommmcek » 24 Jan 2021, 23:33

SundayProgrammer wrote:
24 Jan 2021, 19:22
...it seems doesn't go well with different text box size...
Now it should work (updated the code)
P.s.: Font size is still hardcoded, but can be fixed easily, or?
ozzii
Posts: 481
Joined: 30 Oct 2013, 06:04

Re: Text Filter

Post by ozzii » 25 Jan 2021, 05:02

rommmcek wrote:
24 Jan 2021, 10:27
I'm working (very slowly) on... see pic
Verry nice :bravo:
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: Text Filter

Post by SundayProgrammer » 06 Feb 2021, 23:18

added a new feature, the assorted matches, which can present a summary of the grouped result in chronological or alphabetical order.

try enter this \w*the\w* and hit !a and !a again (and again ...)

Code: Select all

HelpPage=
(
H e l p _ P a g e

F1 - toggle this help page

Basic syntax by example:
	coexisting words/strings/patterns -unwanted
	for instance, d.*d -the and -oo \ws
	(the 5 segments (space separated) above can go all at once, and yet, they can also be entered one by one. that's the so-called multilevel approach, instead.)

Feature keys:
	Alt+a - to show assorted matches and toggle between chronological and alphabetical order.
	WheelDown - to find the next match (downward) from the caret position (given that, the mouse pointer is currently outside the big text/edit box.)
	WheelUp - to find the next match (upward) from the caret position (given that, the mouse pointer is currently outside the big text/edit box.)
	Esc - to quit/exit/leave. same as closing the window.
	Tab - to change focus between the text/edit boxes (just try it.)

	these below only work when the big text/edit box is in focus:
		f - to find the next match (downward) from the caret position.
		Shift+f - to find the next match (upward) from the caret position.
		(note: "the line" refers to the current line, that's where the caret is on.)
		Enter - to copy the line.
		Space - to page down
		Shift+Space - to page up
		s - to move the caret to the beginning/start of the line.
		e - to move the caret to the end of the line.
		t - to move the caret to the top of the text.
		b - to move the caret to the bottom of the text.
		(side note: "caret" may be known as "cursor" to some people.)

	these below only work when the small text/edit box is in focus:
		Enter - to commit a level (which makes the current result becoming the base/source of the next level.) same as clicking the button Go.
		(note: scroll works only when the mouse pointer is currently on the big text/edit box.)
		Down (arrow) - to scroll the result toward the bottom.
		Up (arrow) - to scroll the result toward the top.
		Right (arrow) - to scroll the result toward the end of the line.
		Left (arrow) - to scroll the result toward the beginning of the line.

	Backtick (``) - since users mostly use this tool to locate the specific line of text and copy it for whatever subsequent actions to be taken, which typically involves Tab and then Enter (if the first line is the one wanted), backtick is an alternative which can supersede both keys by just hit backtick twice instead.
End of this help page.
)
StringReplace, HelpPage, HelpPage, `n, `r`n, All
Haystack=
(
The Road Not Taken
BY ROBERT FROST
Two roads diverged in a yellow wood,
And sorry I could not travel both
And be one traveler, long I stood
And looked down one as far as I could
To where it bent in the undergrowth;
Then took the other, as just as fair,
And having perhaps the better claim,
Because it was grassy and wanted wear;
Though as for that the passing there
Had worn them really about the same,
And both that morning equally lay
In leaves no step had trodden black.
Oh, I kept the first for another day!
Yet knowing how way leads on to way,
I doubted if I should ever come back.
I shall be telling this with a sigh
Somewhere ages and ages hence:
Two roads diverged in a wood, and I—
I took the one less traveled by,
And that has made all the difference.
)
n := 1, pn := n - 1, Haystack%n% := Haystack%pn% := Haystack
regFayt := regCase := 1, regWhole := regLinum := regWwrap := 0
theGui:
Gui, tf:New
Gui, Font, s10, Arial New
Gui, Add, Text,, O p t i o n s:
begFayt := regFayt ? "Checked" : ""
Gui, Add, Checkbox, ym vFayt %begFayt% Tabstop gtfFaytHdlr, Instant Filter (aka Find As You Type)
begCase := regCase ? "Checked" : ""
Gui, Add, Checkbox, ym vCase %begCase% Tabstop gtfOptsHdlr, Case Insensitive
begWhole := regWhole ? "Checked" : ""
Gui, Add, Checkbox, ym vWhole %begWhole% Tabstop gtfOptsHdlr, Whole Word
begLinum := regLinum ? "Checked" : ""
Gui, Add, Checkbox, ym vLinum %begLinum% Tabstop gtfLineHdlr, Line Number
beg_Wrap := regWwrap ? "Checked" : ""
Gui, Add, Checkbox, ym vWwrap %beg_Wrap% Tabstop gtfWrapHdlr, Word Wrap
Gui, Font, s18, Arial New
Gui, Add, Text, xm Section, Filter:
w := A_ScreenWidth - 257
Gui, Add, Edit, w%w% ys gtfFpatHdlr +hwndhEdit1
Gui, Add, Button, h38 ys Default, Go
Gui, Add, Button, h38 ys, Back
h := A_ScreenHeight - 143, w := A_ScreenWidth - 30
begWwrap := regWwrap ? "" : "HScroll"
Gui, Font, s18, Consolas
Gui, Add, Edit, ReadOnly x10 h%h% w%w% %begWwrap% VScroll +hwndhEdit2
Gui, Show,, The Text Filter,, The Text Filter - Level %n%   ( Base %blc% Lines )
ControlGet, cid, hwnd,, Edit2, A
SkipWork = 0
RenewGui:
GuiControl,, Edit2, % Haystack%n%
ControlFocus, Edit1, A
SetWinTitle:
StrReplace(Haystack%n%, "`n",, blc), blc += StrLen(Haystack%n%) and SubStr(Haystack%n%, 0) != "`n" ? 1 : 0
WinSetTitle, The Text Filter,, The Text Filter - Level %n%   ( Base %blc% Lines )
Return
tfFaytHdlr:
	regFayt := Fayt()
	If regFayt
		If (Haystack%n% != Haystack%pn%)
		{	Needle%n% := Fpat()
			theHaystack := Haystack%n%, theNeedle := Needle%n%
			n += 1, pn := n - 1
			Haystack%n% := theHaystack, Needle%n% := theNeedle
			Gosub, SetWinTitle
		}
tfOptsHdlr:
	ControlFocus, Edit1, A
	Goto, tfFpatHdlr
tfLineHdlr:
	If regLinum := Linum()
	{	Progress, zh0 w300 c10 fs18, `nHang On...`n,, Working On Line Numbering, Segoe UI
		StrReplace(Haystack, "`n",, oc), oc += StrLen(Haystack) and SubStr(Haystack, 0) != "`n" ? 1 : 0, lsl := StrLen(oc)
		HaystackN =
		Loop, Parse, Haystack, `n
			HaystackN .= SubStr(StrRepeat(A_Space, lsl) A_Index, 1-lsl) ":" A_Space A_LoopField "`n"
		Progress, Off
		Haystack0 := HaystackN
	}Else Haystack0 := Haystack
	Loop, % n
	{	nn := A_Index, pn := nn - 1
		Haystack%nn% := RegExReplace(Haystack%pn%, Maker(Needle%nn%))
	}
	Goto, RenewGui
tfWrapHdlr:
	regWwrap := Wwrap()
	Needle%n% := Fpat()
	Gui, tf:Destroy
	Gosub, theGui
	SkipWork = 1
	GuiControl,, Edit1, % Needle%n%
	SendMessage, 0xB1, cp:=StrLen(Needle%n%), cp,, ahk_id %hEdit1%	;EM_SETSEL	;Send, {End}
	Return
tfButtonGo:
	ControlFocus, Edit1, A
	Progress, zh0 w380 c10 fs18, `nCommitting This Level`n,, Prompt, Segoe UI
	CommitLevel = 1
tfFpatHdlr:
	If SkipWork
	{	SkipWork = 0
		Return
	}Else If not regFayt and not CommitLevel
		Return
	Gui, Submit, NoHide
	regCase := Case(), regWhole := Whole()
	Needle%n% := Fpat()
	If not StrLen(Needle%n%) and CommitLevel
	{	CommitLevel = 0
		Goto, tfButtonBack
	}
	theHaystack := RegExReplace(Haystack%pn%, Maker(Needle%n%))
	If CommitLevel
	{	If (theHaystack = Haystack%pn%)
		{	Progress, Off
			MsgBox, Result No Change Thus Not Anew Level
		}Else
		{	If not regFayt
				Haystack%n% := theHaystack
			If (blc < 2) and not (Needle%n% == Needle%pn%)
				Progress, zh0 w450 c10 fs18, `nUltimate/Empty Result Reached`nThus Level Not Advanced`n,, Prompt, Segoe UI
			Else
			{	n += 1, pn := n - 1
				Haystack%n% := theHaystack
				SendMessage, 0xB1, 0, StrLen(Needle%n%) - 1,, ahk_id %hEdit1%	;EM_SETSEL	;Send, ^a
			}
		}
		CommitLevel = 0
		Sleep, 500
		Progress, Off
	}Else Haystack%n% := theHaystack
	Goto, RenewGui
	Maker(t) {
		r := (Case() ? "i" : "") "m`a)^(?!"
		Loop, Parse, t, % A_Space
			If SubStr(A_LoopField, 1, 1) = "-"
				r .= "(?!.*" (Whole() ? "\b" : "") SubStr(A_LoopField, 2) (Whole() ? "\b" : "") ")"
			Else r .= "(?=.*" (Whole() ? "\b" : "") A_LoopField (Whole() ? "\b" : "") ")"
		r .= ").*\R?"
		Return r
	}
tfButtonBack:
	ControlFocus, Edit1, A
	Needle%n% := Fpat()
	If (n = 1) and (Haystack1 != Haystack0)
		Haystack1 := Haystack0, Needle1 := ""
	Else If (Haystack%n% = Haystack%pn%) and (Needle%n% = Needle%pn%)
		If n > 2
			n -= 2, pn := n - 1
		Else If n = 2
			n := 1, pn := n - 1, Haystack1 := Haystack0, Needle1 := ""
		Else n = 0
	Else n -= 1, pn := n - 1
	If n
	{	Gosub, RenewGui
		SkipWork = 1
		GuiControl,, Edit1, % Needle%n%
		SendMessage, 0xB1, 0, StrLen(Needle%n%),, ahk_id %hEdit1%	;EM_SETSEL	;Send, ^a
		If (n = 1) and (Haystack1 = Haystack0)
		{	Progress, zh0 w380 c10 fs18, `nReturned To The Origin`n,, Prompt, Segoe UI
			Sleep, 500
			Progress, Off
		}Else
		{	Progress, zh0 w380 c10 fs18, `nReturned To Level %n%`n,, Prompt, Segoe UI
			Sleep, 500
			Progress, Off
		}
		Return
	}Else Goto, tfGuiClose
tfGuiEscape:
tfGuiClose:
	ExitApp

Fpat() {
	GuiControlGet, Fpat,, Edit1
	Return Fpat
}
Fayt() {
	GuiControlGet, Fayt,, Button1
	Return Fayt
}
Case() {
	GuiControlGet, Case,, Button2
	Return Case
}
Whole() {
	GuiControlGet, Whole,, Button3
	Return Whole
}
Linum() {
	GuiControlGet, Linum,, Button4
	Return Linum
}
Wwrap() {
	GuiControlGet, Wwrap,, Button5
	Return Wwrap
}

StrRepeat(string, times) {
	Loop, %times%
		output .= string
	Return output
}

FocusedControl() {
	ControlGetFocus, foco
	Return foco
}
#If, WinActive("The Text Filter") and FocusedControl() = "Edit1"
	`::
	Tab::ControlFocus, Edit2, A
	~Up::WheelUp
	~Down::WheelDown
	~Left::WheelLeft
	~Right::WheelRight
	~Bs::
	~Del::
		If regFayt
			Goto, tfFpatHdlr
		Else Return
#If, WinActive("The Text Filter") and FocusedControl() = "Edit2"
	Tab::ControlFocus, Edit1, A
	~Space::PgDn
	~+Space::PgUp
	~s::Home
	~e::End
	~t::^Home
	~b::^End
	`::
	~Enter::
		ControlGetText, Hays, Edit2, A
		DllCall("User32\SendMessage", "Ptr", cid, "UInt", 0x00B0, "UIntP", slcs, "UIntP", slce, "Ptr")
		mpos := slce + 1
		While, mpos > 1 and SubStr(Hays, mpos - 1, 1) not = "`n"
			mpos--
		slcs := mpos, mpos := slce + 1, pmax := StrLen(Hays)
		While, mpos <= pmax and not InStr("`r`n", SubStr(Hays, mpos, 1))
			mpos++
		Clipboard := RegExReplace((mpos > pmax ? SubStr(Hays, slcs) : SubStr(Hays, slcs, mpos - slcs)), "^\t?(?: *?\d+?: )?(.*)$", "$1")
		MsgBox,,, Has Put This Line Into Clipboard, 3
		Goto, tfGuiClose
	~f::
	~+f::
		NextMatch:
		ControlGettext, sPat, Edit1, A
		If StrLen(sPat)
			Gosub, MakerFnm
		Else
		{	ControlGet, sPat, Selected,, Edit2, A
			r := StrLen(sPat) ? (regCase ? "i)" : "") (regWhole ? "\b" : "") sPat (regWhole ? "\b" : "") : ""
		}
		If StrLen(r)
		{	If SubStr(r, 0) = "|"
				r := SubStr(r, 1, StrLen(r)-1)
			ControlGettext, Hays, Edit2, A
			If RegExMatch(Hays, r)
			{	StartHere:
				Gosub, Compute_cp
				If InStr("~WheelUp,~+f", A_ThisHotkey)
				{	Gosub, ReverseWay
					mpos -= 1
				}Else mpos := RegExMatch(Hays, r, nm, cp) - 1
				If mpos >= 0
				{	cp := mpos + StrLen(nm)
					SendMessage, 0xB1, mpos, cp,, ahk_id %hEdit2%	;EM_SETSEL
					SendMessage, 0xB7, 0, 0,, ahk_id %hEdit2%	;EM_SCROLLCARET
				}Else If (mpos < 0) and not InStr("~WheelUp,~+f", A_ThisHotkey)
				{	SendMessage, 0xB1, 0, 0,, ahk_id %hEdit2%	;EM_SETSEL	;Send, ^{Home}
					Goto, StartHere
				}
			}Else MsgBox, Target Not Found
		}
		Return
		MakerFnm:
			r := regCase ? "i)" : ""
			Loop, Parse, sPat, % A_Space
				If SubStr(A_LoopField, 1, 1) != "-"
					r .= (regWhole ? "\b" : "") A_LoopField (regWhole ? "\b" : "") "|"
			Return
		Compute_cp:
			DllCall("User32\SendMessage", "Ptr", cid, "UInt", 0x00B0, "UIntP", slcs, "UIntP", slce, "Ptr"), slcs++, slce++
			cp := InStr("~WheelUp,~+f", A_ThisHotkey) ? slcs : slce
			Return
		ReverseWay:
			If cp > 1
			{	mpos := 0, pmax := cp - 1
				Loop
				{	npos := RegExMatch(Hays, r, pm, mpos+1)
					If npos between 1 and %pmax%
						mpos := npos, nm := pm
					Else Break
				}
				If (npos > pmax) and mpos > 0
					Return
			}
			mpos := cp - 1, pmax := StrLen(Hays)
			Loop
			{	npos := RegExMatch(Hays, r, pm, mpos+1)
				If npos between %cp% and %pmax%
					mpos := npos, nm := pm
				Else Break
			}
			Return
#IfWinActive, The Text Filter
	F1::
		ControlGetText, Hays, Edit2, A
		If SubStr(Hays, 1, 17) = "H e l p _ P a g e"
			ControlSetText, Edit2, %B4Help%, A
		Else
		{	B4Help := Hays
			ControlSetText, Edit2, %HelpPage%, A
		}
		Return
	~Wheelup::
	~Wheeldown::
		MouseGetPos,,,, moc
		If (moc != "Edit2")
		{	ControlFocus, Edit2, A
			Goto, NextMatch
		}Else Return
	!a::
		ControlGetText, Hays, Edit2, A
		If SubStr(Hays, 1, 19) = "Assorted Matches:`t("
		{	If SubStr(Hays, 20, 12) = "Alphabetical"
				ControlSetText, Edit2, %AssHays%, A
			Else
				ControlSetText, Edit2, %AAssHays%, A
			Return
		}
		If not StrLen(Haystack%n%)
			Return
		ControlGetText, sPat, Edit1, A
		If not StrLen(sPat)
			Return
		Gosub, MakerFnm
		If not StrLen(r)
			Return
		Progress, zh0 w300 c10 fs18, `nHang On...`n,, Working On Assortment, Segoe UI
		If SubStr(r, 0) = "|"
			r := SubStr(r, 1, StrLen(r)-1)
		rn = `r`n
		UniqMatches := [], Seq := 0
		Loop, Parse, Haystack%n%, `n
		{	mpos = 1
			worknm:
			npos := RegExMatch(A_LoopField, r, nm, mpos)
			If npos
			{	If UniqMatches.HasKey(nm)
					Ums := UniqMatches[nm], AssHays%Ums% .= InStr(AssHays%Ums%, A_LoopField) ? "" : rn "`t" A_LoopField
				Else
					Seq++, UniqMatches[nm] := Seq, AssHays%Seq% := nm rn "`t" A_LoopField
				mpos := npos + StrLen(nm)
				Goto, worknm
			}
		}
		If Seq
		{	UniqHays := AssHays := "", umNoSort := []
			For k, v in UniqMatches
				UniqHays .= rn StrRepeat(A_Space, 3) A_Index ".`t" k, AssHays .= rn A_Index ". " AssHays%v%, umNoSort[v] ? umNoSort[v].Insert(k) : umNoSort[v] := k
			AAssHays := "Assorted Matches:`t(Alphabetical Order)" UniqHays rn StrRepeat("-", 50) AssHays
			UniqHays := AssHays := ""
			For k, v in umNoSort
				UniqHays .= rn StrRepeat(A_Space, 3) k ".`t" v, AssHays .= rn A_Index ". " AssHays%k%
			AssHays := "Assorted Matches:`t(Chronological Order)" UniqHays rn StrRepeat("-", 50) AssHays
			ControlSetText, Edit2, %AssHays%, A
		}
		Progress, Off
		Return
#IfWinActive
added a help page as @ozzii proposed.
a new bug is fixed.
Last edited by SundayProgrammer on 10 Feb 2021, 06:15, edited 2 times in total.
User avatar
Kellyzkorner_NJ
Posts: 84
Joined: 20 Oct 2017, 18:33

Re: Text Filter

Post by Kellyzkorner_NJ » 08 Feb 2021, 00:26

Very cool! Thank you!
ozzii
Posts: 481
Joined: 30 Oct 2013, 06:04

Re: Text Filter

Post by ozzii » 08 Feb 2021, 05:23

@SundayProgrammer
Really good.

Can you put all the shortcuts into a comment in the top of the script.
Hard to keep all of it in my head..... So in the next update I don't have to copy past them.
Or maybe a line under the options bar. Or an help menu.....
What do you think about that?
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: Text Filter

Post by SundayProgrammer » 08 Feb 2021, 20:33

ozzii wrote:
08 Feb 2021, 05:23
@SundayProgrammer
Really good.

Can you put all the shortcuts into a comment in the top of the script.
Hard to keep all of it in my head..... So in the next update I don't have to copy past them.
Or maybe a line under the options bar. Or an help menu.....
What do you think about that?
@ozzii yes, i think it's better to have one, albeit this is somewhat a challenge to my laziness, indeed. i may not be able to overcome it normally (coz documentation is kinda pain to me.) anyhow, since you proposed and also for the sake of completeness, here you go. the script is updated. plz try it by f1.

hope it helps.
ozzii
Posts: 481
Joined: 30 Oct 2013, 06:04

Re: Text Filter

Post by ozzii » 09 Feb 2021, 04:56

@SundayProgrammer
Thank you.... this is great.... :dance: :bravo:
Maybe one suggestion. with the F1, put the gui automaticaly in the 'word wrap' mode.
But if it's too much work, leave it like this ;) (this can also be changed in the code directly for the personal use....)
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: Text Filter

Post by SundayProgrammer » 10 Feb 2021, 06:17

the copy line under the feature key {Enter} has been bumped since the assorted matches was added. now, it's been fixed (the script is updated) and also changed the method in retrieving the line by the way. cheers.
hasantr
Posts: 933
Joined: 05 Apr 2016, 14:18
Location: İstanbul

Re: Text Filter

Post by hasantr » 24 Feb 2021, 04:25

@rommmcek
Is there any way to start the matching from the beginning of the word?
It can start after a carriage return or a space or special character and end anywhere in the word.
This:
Text := "Fox .Foxing ?Fox _Foxcon Trifox"
Neddle := "Fox"

Fox Match
.Foxing Match
?Fox Match
Foxcon Match
_Foxcon Trifox

Trifox NoMatch
Edit:

I think he wants to use this pattern

Code: Select all

U)(?=[\w])([^_])(?<=[\w^_])
User avatar
rommmcek
Posts: 1470
Joined: 15 Aug 2014, 15:18

Re: Text Filter

Post by rommmcek » 24 Feb 2021, 08:17

Needle input of the Text Filter is designed by the SundayProgrammer to except Regex syntax (probably not very complex one). But you have to consider that space is treated as separator (like | in Regex).
So:
- to match space use \s
- to match reserved Regex character use \., \? etc.
- to match start of the word ^\s\.fox or ^\s\?fox

TriFox cannot match Fox, only TriFox
^TriFox won't match any line in your haystack
etc.

I completely don't understand U)(?=[\w])([^_])(?<=[\w^_]). Did you write it by yourself? If not, can you provide a link? Anyways I would not recommend to use very complex Regex needle in Text Filter.
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: Text Filter

Post by SundayProgrammer » 07 Mar 2021, 01:30

added an input history dropdown by using the combobox control.

some minor consequences: 1, those feature keys for scrolling are now all removed to avoid conflicts with the native combobox navigation. and 2, the wheeldown/up for "find next match" only work when the mouse pointer is outside both text/edit boxes. 3, some of the "sendmessage 0xB1" doesn't work with this change, hence reversed to using "send" for the time being.

Code: Select all

HelpPage=
(
H e l p _ P a g e

F1 - to toggle this help page

Basic syntax by example:
	coexisting words/strings/patterns -unwanted
	for instance, d.*d -the and -oo \ws
	(the 5 segments (space separated) above can go all at once, and yet, they can also be entered one by one. that's the so-called multilevel approach, instead.)

Feature keys:
	Alt+a - to show assorted matches and toggle between chronological and alphabetical order.
	WheelDown - to find the next match (downward) from the caret position (given that, the mouse pointer is currently outside both text/edit boxes.)
	WheelUp - to find the next match (upward) from the caret position (given that, the mouse pointer is currently outside both text/edit boxes.)
	Esc - to quit/exit/leave. same as closing the window.
	Tab - to change focus between the text/edit boxes (just try it.)

	these below only work when the big text/edit box is in focus:
		f - to find the next match (downward) from the caret position.
		Shift+f - to find the next match (upward) from the caret position.
		(note: "the line" refers to the current line, that's where the caret is on.)
		Enter - to copy the line.
		Space - to page down
		Shift+Space - to page up
		s - to move the caret to the beginning/start of the line.
		e - to move the caret to the end of the line.
		t - to move the caret to the top of the text.
		b - to move the caret to the bottom of the text.
		(side note: "caret" may be known as "cursor" to some people.)

	these below only work when the small text/edit box is in focus:
		Enter - to commit a level (which makes the current result becoming the base/source of the next level.) same as clicking the button Go.

	Backtick (``) - since users mostly use this tool to locate the specific line of text and copy it for whatever subsequent actions to be taken, which typically involves Tab and then Enter (if the first line is the one wanted), backtick is an alternative which can supersede both keys by just hit backtick twice instead.
End of this help page.
)
StringReplace, HelpPage, HelpPage, `n, `r`n, All
Haystack=
(
The Road Not Taken
BY ROBERT FROST
Two roads diverged in a yellow wood,
And sorry I could not travel both
And be one traveler, long I stood
And looked down one as far as I could
To where it bent in the undergrowth;
Then took the other, as just as fair,
And having perhaps the better claim,
Because it was grassy and wanted wear;
Though as for that the passing there
Had worn them really about the same,
And both that morning equally lay
In leaves no step had trodden black.
Oh, I kept the first for another day!
Yet knowing how way leads on to way,
I doubted if I should ever come back.
I shall be telling this with a sigh
Somewhere ages and ages hence:
Two roads diverged in a wood, and I—
I took the one less traveled by,
And that has made all the difference.
)
n := 1, pn := n - 1, Haystack%n% := Haystack%pn% := Haystack, NeedleHistory := ""
regFayt := regCase := 1, regWhole := regLinum := regWwrap := 0
theGui:
Gui, tf:New
Gui, +Delimiter`n
Gui, Font, s10, Arial New
Gui, Add, Text,, O p t i o n s:
begFayt := regFayt ? "Checked" : ""
Gui, Add, Checkbox, ym vFayt %begFayt% Tabstop gtfFaytHdlr, Instant Filter (aka Find As You Type)
begCase := regCase ? "Checked" : ""
Gui, Add, Checkbox, ym vCase %begCase% Tabstop gtfOptsHdlr, Case Insensitive
begWhole := regWhole ? "Checked" : ""
Gui, Add, Checkbox, ym vWhole %begWhole% Tabstop gtfOptsHdlr, Whole Word
begLinum := regLinum ? "Checked" : ""
Gui, Add, Checkbox, ym vLinum %begLinum% Tabstop gtfLineHdlr, Line Number
beg_Wrap := regWwrap ? "Checked" : ""
Gui, Add, Checkbox, ym vWwrap %beg_Wrap% Tabstop gtfWrapHdlr, Word Wrap
Gui, Font, s18, Arial New
Gui, Add, Text, xm Section, Filter:
w := A_ScreenWidth - 257
Gui, Add, ComboBox, w%w% ys vNeedleCB gtfFpatHdlr +hwndhEdit1
Gui, Add, Button, h38 ys Default, Go
Gui, Add, Button, h38 ys, Back
h := A_ScreenHeight - 143, w := A_ScreenWidth - 30
begWwrap := regWwrap ? "" : "HScroll"
Gui, Font, s18, Consolas
Gui, Add, Edit, ReadOnly x10 h%h% w%w% %begWwrap% VScroll +hwndhEdit2
Gui, Show,, The Text Filter,, The Text Filter - Level %n%   ( Base %blc% Lines )
ControlGet, cid, hwnd,, Edit2, A
RenewGui:
GuiControl,, Edit2, % Haystack%n%
ControlFocus, Edit1, A
SetWinTitle:
StrReplace(Haystack%n%, "`n",, blc), blc += StrLen(Haystack%n%) and SubStr(Haystack%n%, 0) != "`n" ? 1 : 0
WinSetTitle, The Text Filter,, The Text Filter - Level %n%   ( Base %blc% Lines )
Return
tfFaytHdlr:
	regFayt := Fayt()
	If regFayt
		If (Haystack%n% != Haystack%pn%)
		{	Needle%n% := Fpat()
			theHaystack := Haystack%n%, theNeedle := Needle%n%
			n += 1, pn := n - 1
			Haystack%n% := theHaystack, Needle%n% := theNeedle
			Gosub, SetWinTitle
		}
tfOptsHdlr:
	ControlFocus, Edit1, A
	Goto, tfFpatHdlr
tfLineHdlr:
	If regLinum := Linum()
	{	Progress, zh0 w300 c10 fs18, `nHang On...`n,, Working On Line Numbering, Segoe UI
		StrReplace(Haystack, "`n",, oc), oc += StrLen(Haystack) and SubStr(Haystack, 0) != "`n" ? 1 : 0, lsl := StrLen(oc)
		HaystackN =
		Loop, Parse, Haystack, `n
			HaystackN .= SubStr(StrRepeat(A_Space, lsl) A_Index, 1-lsl) ":" A_Space A_LoopField "`n"
		Progress, Off
		Haystack0 := HaystackN
	}Else Haystack0 := Haystack
	Loop, % n
	{	nn := A_Index, pn := nn - 1
		Haystack%nn% := RegExReplace(Haystack%pn%, Maker(Needle%nn%))
	}
	Goto, RenewGui
tfWrapHdlr:
	regWwrap := Wwrap()
	Needle%n% := Fpat()
	Gui, tf:Destroy
	Gosub, theGui
	ControlSetText, Edit1, % Needle%n%, A
	Send, {End}	;SendMessage, 0xB1, cp:=StrLen(Needle%n%), cp,, ahk_id %hEdit1%	;EM_SETSEL
	Return
tfButtonGo:
	ControlFocus, Edit1, A
	Progress, zh0 w380 c10 fs18, `nCommitting This Level`n,, Prompt, Segoe UI
	CommitLevel = 1
tfFpatHdlr:
	If not regFayt and not CommitLevel
		Return
	Gui, Submit, NoHide
	regCase := Case(), regWhole := Whole()
	Needle%n% := Fpat()
	If not StrLen(Needle%n%) and CommitLevel
	{	CommitLevel = 0
		Goto, tfButtonBack
	}
	theHaystack := RegExReplace(Haystack%pn%, Maker(Needle%n%))
	If CommitLevel
	{	If not StrLen(NeedleHistory)
			NeedleHistory := Needle%n%, Added := 1
		Else If not InStr(NeedleHistory, Needle%n%, !regCase)
			NeedleHistory := Needle%n% "`n" NeedleHistory, Added := 1
		Else Added := 0
		If Added
		{	GuiControl,, NeedleCB, `n%NeedleHistory%
			ControlSetText, Edit1, % Needle%n%
		}
		If (theHaystack = Haystack%pn%)
		{	Progress, Off
			MsgBox, Result No Change Thus Not Anew Level
		}Else
		{	If not regFayt
				Haystack%n% := theHaystack
			If (blc < 2) and not (Needle%n% == Needle%pn%)
				Progress, zh0 w450 c10 fs18, `nUltimate/Empty Result Reached`nThus Level Not Advanced`n,, Prompt, Segoe UI
			Else
			{	n += 1, pn := n - 1
				Haystack%n% := theHaystack
				Send, ^a	;SendMessage, 0xB1, 0, StrLen(Needle%pn%),, ahk_id %hEdit1%	;EM_SETSEL
			}
		}
		CommitLevel = 0
		Sleep, 500
		Progress, Off
	}Else Haystack%n% := theHaystack
	Goto, RenewGui
	Maker(t) {
		r := (Case() ? "i" : "") "m`a)^(?!"
		Loop, Parse, t, % A_Space
			If SubStr(A_LoopField, 1, 1) = "-"
				r .= "(?!.*(" (Whole() ? "\b" : "") SubStr(A_LoopField, 2) (Whole() ? "\b" : "") "))"
			Else r .= "(?=.*(" (Whole() ? "\b" : "") A_LoopField (Whole() ? "\b" : "") "))"
		r .= ").*\R?"
		Return r
	}
tfButtonBack:
	ControlFocus, Edit1, A
	Needle%n% := Fpat()
	If (n = 1) and (Haystack1 != Haystack0)
		Haystack1 := Haystack0, Needle1 := ""
	Else If (Haystack%n% = Haystack%pn%) and (Needle%n% = Needle%pn%)
		If n > 2
			n -= 2, pn := n - 1
		Else If n = 2
			n := 1, pn := n - 1, Haystack1 := Haystack0, Needle1 := ""
		Else n = 0
	Else n -= 1, pn := n - 1
	If n
	{	Gosub, RenewGui
		ControlSetText, Edit1, % Needle%n%
		Send, ^a	;SendMessage, 0xB1, 0, StrLen(Needle%n%),, ahk_id %hEdit1%	;EM_SETSEL
		If (n = 1) and (Haystack1 = Haystack0)
		{	Progress, zh0 w380 c10 fs18, `nReturned To The Origin`n,, Prompt, Segoe UI
			Sleep, 500
			Progress, Off
		}Else
		{	Progress, zh0 w380 c10 fs18, `nReturned To Level %n%`n,, Prompt, Segoe UI
			Sleep, 500
			Progress, Off
		}
		Return
	}Else Goto, tfGuiClose
tfGuiEscape:
tfGuiClose:
	ExitApp

Fpat() {
	ControlGetText, Fpat, Edit1	;GuiControlGet, Fpat,, Edit1
	Return Fpat
}
Fayt() {
	GuiControlGet, Fayt,, Button1
	Return Fayt
}
Case() {
	GuiControlGet, Case,, Button2
	Return Case
}
Whole() {
	GuiControlGet, Whole,, Button3
	Return Whole
}
Linum() {
	GuiControlGet, Linum,, Button4
	Return Linum
}
Wwrap() {
	GuiControlGet, Wwrap,, Button5
	Return Wwrap
}

escrx(h) {
	e := "\().[]*+?{}^$|"

	Loop, Parse, e
		If InStr(h, A_LoopField)
			h := StrReplace(h, A_LoopField, "\" A_LoopField)

	Return h
}

StrRepeat(string, times) {
	Loop, %times%
		output .= string
	Return output
}

FocusedControl() {
	ControlGetFocus, foco
	Return foco
}
#If, WinActive("The Text Filter") and FocusedControl() = "Edit1"
	`::
	Tab::ControlFocus, Edit2, A
	~Bs::
	~Del::
		If regFayt
			Goto, tfFpatHdlr
		Else Return
#If, WinActive("The Text Filter") and FocusedControl() = "Edit2"
	Tab::ControlFocus, Edit1, A
	~Space::PgDn
	~+Space::PgUp
	~s::Home
	~e::End
	~t::^Home
	~b::^End
	`::
	~Enter::
		ControlGetText, Hays, Edit2, A
		DllCall("User32\SendMessage", "Ptr", cid, "UInt", 0x00B0, "UIntP", slcs, "UIntP", slce, "Ptr")
		mpos := slce + 1
		While, mpos > 1 and SubStr(Hays, mpos - 1, 1) not = "`n"
			mpos--
		slcs := mpos, mpos := slce + 1, pmax := StrLen(Hays)
		While, mpos <= pmax and not InStr("`r`n", SubStr(Hays, mpos, 1))
			mpos++
		Clipboard := RegExReplace((mpos > pmax ? SubStr(Hays, slcs) : SubStr(Hays, slcs, mpos - slcs)), "^\t?(?: *?\d+?: )?(.*)$", "$1")
		MsgBox,,, Has Put This Line Into Clipboard, 3
		Goto, tfGuiClose
	~f::
	~+f::
		NextMatch:
		ControlGettext, sPat, Edit1, A
		If StrLen(sPat)
			Gosub, MakerFnm
		Else
		{	ControlGet, sPat, Selected,, Edit2, A
			r := StrLen(sPat) ? (regCase ? "i)" : "") (regWhole ? "\b" : "") escrx(sPat) (regWhole ? "\b" : "") : ""
		}
		If StrLen(r)
		{	If SubStr(r, 0) = "|"
				r := SubStr(r, 1, StrLen(r)-1)
			ControlGettext, Hays, Edit2, A
			If RegExMatch(Hays, r)
			{	StartHere:
				Gosub, Compute_cp
				If InStr("~WheelUp,~+f", A_ThisHotkey)
				{	Gosub, ReverseWay
					mpos -= 1
				}Else mpos := RegExMatch(Hays, r, nm, cp) - 1
				If mpos >= 0
				{	cp := mpos + StrLen(nm)
					SendMessage, 0xB1, mpos, cp,, ahk_id %hEdit2%	;EM_SETSEL
					SendMessage, 0xB7, 0, 0,, ahk_id %hEdit2%	;EM_SCROLLCARET
				}Else If (mpos < 0) and not InStr("~WheelUp,~+f", A_ThisHotkey)
				{	SendMessage, 0xB1, 0, 0,, ahk_id %hEdit2%	;EM_SETSEL	;Send, ^{Home}
					Goto, StartHere
				}
			}Else MsgBox, Target Not Found
		}
		Return
		MakerFnm:
			r := (regCase ? "i" : "") "m`a)"
			Loop, Parse, sPat, % A_Space
				If SubStr(A_LoopField, 1, 1) != "-"
					r .= "(" (regWhole ? "\b" : "") A_LoopField (regWhole ? "\b" : "") ")|"
			Return
		Compute_cp:
			DllCall("User32\SendMessage", "Ptr", cid, "UInt", 0x00B0, "UIntP", slcs, "UIntP", slce, "Ptr"), slcs++, slce++
			cp := InStr("~WheelUp,~+f", A_ThisHotkey) ? slcs : slce
			Return
		ReverseWay:
			If cp > 1
			{	mpos := 0, pmax := cp - 1
				Loop
				{	npos := RegExMatch(Hays, r, pm, mpos+1)
					If npos between 1 and %pmax%
						mpos := npos, nm := pm
					Else Break
				}
				If (npos > pmax) and mpos > 0
					Return
			}
			mpos := cp - 1, pmax := StrLen(Hays)
			Loop
			{	npos := RegExMatch(Hays, r, pm, mpos+1)
				If npos between %cp% and %pmax%
					mpos := npos, nm := pm
				Else Break
			}
			Return
#IfWinActive, The Text Filter
	F1::
		ControlGetText, Hays, Edit2, A
		If SubStr(Hays, 1, 17) = "H e l p _ P a g e"
			ControlSetText, Edit2, %B4Help%, A
		Else
		{	B4Help := Hays
			ControlSetText, Edit2, %HelpPage%, A
		}
		Return
	~Wheelup::
	~Wheeldown::
		MouseGetPos,,,, moc
		If not StrLen(moc) or not InStr("Edit1|Edit2", moc)
		{	ControlFocus, Edit2, A
			Goto, NextMatch
		}Else Return
	!a::
		ControlGetText, Hays, Edit2, A
		If SubStr(Hays, 1, 19) = "Assorted Matches:`t("
		{	If SubStr(Hays, 20, 12) = "Alphabetical"
				ControlSetText, Edit2, %AssHays%, A
			Else
				ControlSetText, Edit2, %AAssHays%, A
			Return
		}
		If not StrLen(Haystack%n%)
			Return
		ControlGetText, sPat, Edit1, A
		If not StrLen(sPat)
			Return
		Gosub, MakerFnm
		If not StrLen(r)
			Return
		Progress, zh0 w300 c10 fs18, `nHang On...`n,, Working On Assortment, Segoe UI
		If SubStr(r, 0) = "|"
			r := SubStr(r, 1, StrLen(r)-1)
		rn = `r`n
		UniqMatches := [], Seq := 0
		Loop, Parse, Haystack%n%, `n
		{	mpos = 1
			worknm:
			npos := RegExMatch(A_LoopField, r, nm, mpos)
			If npos
			{	If UniqMatches.HasKey(nm)
					Ums := UniqMatches[nm], AssHays%Ums% .= InStr(AssHays%Ums%, A_LoopField) ? "" : rn "`t" A_LoopField
				Else
					Seq++, UniqMatches[nm] := Seq, AssHays%Seq% := nm rn "`t" A_LoopField
				mpos := npos + StrLen(nm)
				Goto, worknm
			}
		}
		If Seq
		{	UniqHays := AssHays := "", umNoSort := []
			For k, v in UniqMatches
				UniqHays .= rn StrRepeat(A_Space, 3) A_Index ".`t" k, AssHays .= rn A_Index ". " AssHays%v%, umNoSort[v] ? umNoSort[v].Insert(k) : umNoSort[v] := k
			AAssHays := "Assorted Matches:`t(Alphabetical Order)" UniqHays rn StrRepeat("-", 50) AssHays
			UniqHays := AssHays := ""
			For k, v in umNoSort
				UniqHays .= rn StrRepeat(A_Space, 3) k ".`t" v, AssHays .= rn A_Index ". " AssHays%k%
			AssHays := "Assorted Matches:`t(Chronological Order)" UniqHays rn StrRepeat("-", 50) AssHays
			ControlSetText, Edit2, %AssHays%, A
		}
		Progress, Off
		Return
#IfWinActive
Last edited by SundayProgrammer on 13 Mar 2021, 18:59, edited 1 time in total.
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: Text Filter

Post by SundayProgrammer » 08 Mar 2021, 19:42

changed to use `n as the delimiter for the combobox list to avoid this: default delimiter ("|") conflicts with a regex operator.
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: Text Filter

Post by SundayProgrammer » 13 Mar 2021, 19:16

1, there was some case sensitivity issues since combobox was used, and now fixed.

2, there was a bug when "|" (the regex "or" operator) was used, and now fixed. (this bug existed since inception which i didn't notice.)

3, search by selection (i.e. by emptying the small text/edit box and highlighting the trunk of text in the big text/edit box you want to "find next match") is now ignoring all regex syntaxes (i.e. search literally) as it's supposed to be.
Post Reply

Return to “Scripts and Functions (v1)”