AHK Workspaces - Window Management Script by Maestrith

Post your working scripts, libraries and tools
capeably
Posts: 31
Joined: 10 Jul 2014, 14:38

AHK Workspaces - Window Management Script by Maestrith

28 Apr 2016, 18:03

Are there certain Workspaces, or groups of windows (applications, file folders, etc.), that you have to open and arrange in a specific way every time you do certain tasks? It can be time consuming if you do this manually. And if you pivot between various tasks and workspaces frequently throughout the day, that time adds up. That's why Maestrith and I came up with this AHK Workspaces script.

With this Workspaces script, you can launch, bring into focus, and arrange any Workspace (combination of applications, windows, or file folders) with a single hotkey combination. Then with that same hotkey combination, you can hide or close those windows so you can focus on something else.

I've been using this every time I boot up my PC for over a year now. It's really a game changer if you need to pivot between tasks and workspaces all throughout the day.

Big thanks to Maestrith for his work on this!

Here's a video demo:



Code is also available via Maestrith's website here:
http://files.maestrith.com/Workspace/Workspace.ahk

Code: Select all

#SingleInstance,Force
global settings:=new xml("settings")
if(FileExist("workspaces.ico"))
	Menu,Tray,Icon,Workspaces.ico
Gui()
return
show:
WinShow,% hwnd([1])
return
capture(){
	static node
	Gui,1:Default
	Gui,1:TreeView,SysTreeView321
	node:=settings.ssn("//*[@tv='" TV_GetSelection() "']"),node:=ssn(node,"ancestor-or-self::workspace")
	if !node
		return m("Please select or create a workspace to add windows to")
	Gui,2:Destroy
	Gui,2:Default
	Gui,Add,ListView,w600 h200,Window|hwnd
	Gui,Add,Button,gchoose Default,Add Selected
	WinGet,list,list
	Loop,%list%{
		WinGetTitle,title,% "ahk_id" list%A_Index%
		WinGet,max,MinMax,% "ahk_id" list%A_Index%
		if(max!=0||title="")
			continue
		if(title="program manager")
			Continue
		WinGetPos,x,y,w,h,% "ahk_id" list%A_Index%
		if title
			LV_Add("",title,list%A_Index%)
	}
	windows:=settings.sn("//window")
	while,ww:=windows.item[A_Index-1],ea:=xml.ea(ww)
		LV_Add("",ea.title)
	Loop,3
		LV_ModifyCol(A_Index,"AutoHDR")
	Gui,Show,,% "Select Windows To Add To : " ssn(node,"@title").text
	return
	choose:
	WorkSpaceState(),next:=0
	Gui,2:Default
	Gui,2:ListView,SysListView321
	while,next:=LV_GetNext(next){
		LV_GetText(win,next,2)
		if(win=""){
			LV_GetText(title,next,1)
			if(ssn(node,"window[@title='" title "']"))
				Continue
			copy:=settings.ssn("//window[@title='" title "']")
			clone:=copy.clonenode(1)
			node.AppendChild(clone),node.SetAttribute("expand",1)
			Continue
		}
		for Item in ComObjCreate("Shell.Application").Windows
			if(item.hwnd=win)
				run:=RegExReplace(SubStr(uridecode(item.locationurl),9),"\/","\")
		aid:="ahk_id" win
		WinGetTitle,title,%aid%
		WinGetClass,class,%aid%
		WinGetPos,x,y,w,h,%aid%
		Position:="x" x " y" y " w" w " h" h
		WinGet,list,list
		if(run=""){
			Loop,%list%{
				hwnd:=list%A_Index%
				WinGetTitle,wintitle,% "ahk_id" hwnd
				if (wintitle==title){
					WinGet,Run,processpath,ahk_id%hwnd%
					Break
				}
			}
		}
		WinGet,maximize,MinMax,%title%
		SysGet,count,MonitorCount
		WinRestore,% hwnd([1])
		WinActivate,% hwnd([1])
		if !ssn(node,"window[@title='" title "']"){
			top:=settings.under({under:node,node:"window",att:{title:title}})
			for a,b in {Class:class,Maximize:0,Run:Run,"Auto Close":0,"Auto Open":0}
				settings.under({under:top,node:"item",att:{title:a,value:b}})
			mc:=settings.under({under:top,node:"monitor",att:{title:"Monitor Count",value:count}})
			settings.under({under:mc,node:"position",att:{title:"Position",value:position}})
			node.SetAttribute("expand",1)
		}
		run:=""
	}
	Gui,2:Destroy
	PopulateGroups()
	return
}
Create_Workspace(){
	createworkspace:
	InputBox,workspace,Workspace Name,Enter the name for the new workspace
	if(ErrorLevel||workspace="")
		return
	if settings.ssn("//workspaces/workspace[@title='" workspace "']")
		return m("Workspace exists.")
	select:=settings.sn("//*[@select]")
	while,ss:=select.item[A_Index-1]
		ss.RemoveAttribute("select")
	ws:=settings.Add({path:"workspaces/workspace",att:{title:workspace,select:1},dup:1})
	Populategroups()
	return
}
delete(){
	delete:
	Gui,1:Default
	Gui,1:TreeView,SysTreeView321
	ControlGetFocus,Focus,% hwnd([1])
	if(Focus="SysTreeView321"){
		sel:=TV_GetSelection(),node:=settings.ssn("//*[@tv='" sel "']"),ea:=xml.ea(node)
		if(node.nodename="workspaces")
			return m("Can not delete the main workspaces")
		if(node.nodename="window"){
			tv:=TV_GetNext(sel)?TV_GetNext(sel):TV_GetPrev(sel)?TV_GetPrev(sel):TV_GetParent(sel)
			TV_Modify(tv,"Select Vis Focus"),node.ParentNode.RemoveChild(node)
			return PopulateGroups(1)
		}
		if(node.nodename="workspace"){
			MsgBox,308,Are you sure?,This action can not be undone.
			IfMsgBox,No
				return
			node.ParentNode.RemoveChild(node),PopulateGroups(1)
		}if(ea.title="run")
			node.SetAttribute("value",""),TV_Modify(sel,"","Run")
		return
		Gui,1:Default
		Gui,1:TreeView,SysTreeView321
		return
		rem:=settings.ssn("//*[@tv='" TV_GetSelection() "']"),rem.ParentNode.RemoveChild(rem)
		PopulateGroups(1)
	}
	return
}
exit(){
	keyexit:
	ExitApp
	return
	Exit:
	WorkSpaceState()
	tv:=settings.sn("//*[@tv]")
	while,tt:=tv.item[A_Index-1]
		tt.RemoveAttribute("tv")
	settings.save(1)
	ExitApp
	return
}
Find(doc,node,find){
	if InStr(find,"'")
		return doc.SelectSingleNode(node "[contains(.,concat('" RegExReplace(find,"'","'," Chr(34) "'" Chr(34) ",'") "'))]/..")
	else
		return doc.SelectSingleNode(node "[.='" find "']/..")
}
Gui(){
	static
	SetTitleMatchMode,2
	Gui,+hwndhwnd
	hwnd(1,hwnd)
	Hotkey,IfWinActive,% hwnd([1])
	Hotkey,Delete,Delete,On
	Hotkey,^down,movedown,On
	Hotkey,^up,moveup,On
	Hotkey,~Enter,Enter,On
	Hotkey,+Escape,keyExit,On
	Gui,Add,TreeView,w500 h400 AltSubmit gtv hwndtv
	Gui,Add,Button,gcreateworkspace,&Create Workspace
	Gui,Add,Button,gcapture,&Add Windows To Selected Workspace
	Gui,Add,Button,gupdatepos,&Update Open Windows Positions
	Gui,Add,Button,gcontract,Con&tract All Workspaces
	Gui,Add,Button,gupdate,Update Program
	Gui,Add,Button,ghelp,&Help
	hwnd("addtoworkspace",atw),hwnd("tv",tv)
	Version=0.001.6
	Gui,Show,,Workspace %version%
	PopulateGroups(),tv()
	OnExit,Exit
	Menu,Tray,NoStandard
	Menu,Tray,Add,Show Workspaces,show
	Menu,Tray,Default,Show Workspaces
	Menu,Tray,Standard
	if win:=settings.ssn("//windows")
		win.ParentNode.RemoveChild(win)
	return
	contract:
	WorkSpaceState(),contract:=settings.sn("//*[@expand]")
	GuiControl,-Redraw,SysTreeView321
	while,cc:=contract.item[A_Index-1]
		if(cc.nodename!="workspaces")
			TV_Modify(ssn(cc,"@tv").text,"-Expand")
	GuiControl,+Redraw,SysTreeView321
	return
	toggle:
	Gui,1:TreeView,SysTreeView321
	Gui,1:Default
	tv:=settings.ssn("//*[@tv='" TV_GetSelection() "']")
	if (tv.nodename!="Workspace")
		return m("Select a Workspace to add windows to")
	Gui,3:Destroy
	Gui,3:Default
	Gui,Add,ListView,w400 h300,Window
	Gui,Add,Button,gaddto Default,% "Add Selected to " ssn(tv,"@title").text
	win:=settings.sn("//windows/*")
	while,ww:=win.item[A_Index-1],ea:=xml.ea(ww)
		LV_Add("",ea.title)
	Gui,Show,,% "Add To " ssn(tv,"@title").text
	return
	addto:
	Gui,3:Default
	Gui,3:ListView,SysListView321
	next:=0
	while,next:=LV_GetNext(next),LV_GetText(name,next)
		if !ssn(tv,"window[@title='" name "']")
			node:=settings.find("//windows/window/@title",name),clone:=node.clonenode(1),tv.AppendChild(clone)
	tv.SetAttribute("expand",1),PopulateGroups()
	return
	3GuiEscape:
	Gui,3:Destroy
	return
}
hwnd(win,hwnd=""){
	static window:=[]
	if win=get
		return window
	if (win.rem){
		Gui,1:-Disabled
		Gui,1:Default
		WindowTracker.Exit(win.rem)
		if !window[win.rem]
			Gui,% win.rem ":Destroy"
		Else
			DllCall("DestroyWindow",uptr,window[win.rem])
		window[win.rem]:=""
	}
	if IsObject(win)
		return "ahk_id" window[win.1]
	if !hwnd
		return window[win]
	window[win]:=hwnd
}
m(x*){
	for a,b in x
		list.=b "`n"
	MsgBox,,AHK Studio,% list
}
t(x*){
	for a,b in x
		list.=b "`n"
	ToolTip,% list
}
PopulateGroups(save:=0){
	static lastkeys:=[]
	Gui,1:Default
	Gui,1:TreeView,SysTreeView321
	if save
		WorkSpaceState()
	GuiControl,1:-Redraw,SysTreeView321
	TV_Delete(),TreeView:=[],lastkeys:=[]
	for a in lastkeys{
		Hotkey,IfWinActive
		Hotkey,%a%,Hotkey,Off
	}
	for a,b in {workspaces:"Workspaces"}{
		info:=settings.sn("//" a "/descendant-or-self::*")
		while,ii:=info.item[A_Index-1],ea:=xml.ea(ii){
			value:=ea.hotkey?" = " ea.hotkey:ea.value!=""?" = " ea.value:"",title:=ea.title?ea.title:b
			ii.SetAttribute("tv",TV_Add(title value,ssn(ii.ParentNode,"@tv").text))
			if(ea.hotkey){
				Hotkey,IfWinActive
				Hotkey,% ea.Hotkey,hotkey,On
				lastkeys[ea.hotkey]:=1
			}
		}
	}
	set:=settings.sn("//Settings/descendant-or-self::*")
	if (set.length!=3){
		top:=settings.Add({path:"Settings",att:{name:"Settings"}})
		for a,b in ["Hide/Show GUI","Toggle Current Workspace"]
			if !ssn(top,"setting[@name='" b "']")
				settings.under({under:top,node:"setting",att:{name:b}})
		set:=settings.sn("//Settings/descendant-or-self::*")
	}
	while,ss:=set.item[A_Index-1],ea:=xml.ea(ss){
		hotkey:=ea.hotkey?" = " ea.hotkey:""
		tv:=TV_Add(ea.name hotkey,ssn(ss.ParentNode,"@tv").text),ss.SetAttribute("tv",tv)
		if(hotkey){
			Hotkey,IfWinActive
			Hotkey,% ea.Hotkey,hotkey,On
			lastkeys[ea.hotkey]:=1
		}
	}
	select:=settings.sn("//*[@select]"),tv:=ssn(Select.item[0],"@tv").text,expand:=settings.sn("//*[@expand]"),VisFirst:=settings.ssn("//*[@VisFirst]/@tv").text
	while,ss:=select.item[A_Index-1]
		ss.RemoveAttribute("select")
	while,ee:=expand.item[A_Index-1],ea:=xml.ea(ee)
		TV_Modify(ea.tv,"Expand")
	select:=settings.sn("//*[@select]"),TV_Modify(tv,"Select Vis Focus")
	TV_Modify(VisFirst,"VisFirst")
	GuiControl,1:+Redraw,SysTreeView321
	return
}
class xml{
	keep:=[]
	__New(param*){
		if !FileExist(A_ScriptDir "\lib")
			FileCreateDir,%A_ScriptDir%\lib
		root:=param.1,file:=param.2
		file:=file?file:root ".xml"
		temp:=ComObjCreate("MSXML2.DOMDocument"),temp.setProperty("SelectionLanguage","XPath")
		this.xml:=temp
		if FileExist(file){
			ff:=FileOpen(file,"r","utf-16"),info:=ff.Read(ff.length)
			if(info=""){
				this.xml:=this.CreateElement(temp,root)
				FileDelete,%file%
			}else
				temp.loadxml(info),this.xml:=temp
		}else
			this.xml:=this.CreateElement(temp,root)
		this.file:=file
		xml.keep[root]:=this
	}
	CreateElement(doc,root){
		return doc.AppendChild(this.xml.CreateElement(root)).parentnode
	}
	lang(info){
		info:=info=""?"XPath":"XSLPattern"
		this.xml.setProperty("SelectionLanguage",info)
	}
	unique(info){
		if (info.check&&info.text)
			return
		if info.under{
			if info.check
				find:=info.under.SelectSingleNode("*[@" info.check "='" info.att[info.check] "']")
			if info.Text
				find:=this.cssn(info.under,"*[text()='" info.text "']")
			if !find
				find:=this.under({under:info.under,att:info.att,node:info.path})
			for a,b in info.att
				find.SetAttribute(a,b)
		}
		else
		{
			if info.check
				find:=this.ssn("//" info.path "[@" info.check "='" info.att[info.check] "']")
			else if info.text
				find:=this.ssn("//" info.path "[text()='" info.text "']")
			if !find
				find:=this.add({path:info.path,att:info.att,dup:1})
			for a,b in info.att
				find.SetAttribute(a,b)
		}
		if info.text
			find.text:=info.text
		return find
	}
	Add(info){
		path:=info.path,p:="/",dup:=this.ssn("//" path)?1:0
		if next:=this.ssn("//" path)?this.ssn("//" path):this.ssn("//*")
			Loop,Parse,path,/
				last:=A_LoopField,p.="/" last,next:=this.ssn(p)?this.ssn(p):next.appendchild(this.xml.CreateElement(last))
		if (info.dup&&dup)
			next:=next.parentnode.appendchild(this.xml.CreateElement(last))
		for a,b in info.att
			next.SetAttribute(a,b)
		for a,b in StrSplit(info.list,",")
			next.SetAttribute(b,info.att[b])
		if info.text!=""
			next.text:=info.text
		return next
	}
	under(info){
		new:=info.under.appendchild(this.xml.createelement(info.node))
		for a,b in info.att
			new.SetAttribute(a,b)
		for a,b in StrSplit(info.list,",")
			new.SetAttribute(b,info.att[b])
		if info.text
			new.text:=info.text
		return new
	}
	findsn(node,find){
		if InStr(find,"'")
			return this.xml.SelectNodes(node "[contains(.,concat('" RegExReplace(find,"'","'," Chr(34) "'" Chr(34) ",'") "'))]/..")
		else
			return this.xml.SelectNodes(node "[.='" find "']/..")
	}
	find(node,find){
		if InStr(find,"'")
			return this.xml.SelectSingleNode(node "[contains(.,concat('" RegExReplace(find,"'","'," Chr(34) "'" Chr(34) ",'") "'))]/..")
		else
			return this.xml.SelectSingleNode(node "[.='" find "']/..")
	}
	ssn(node){
		return this.xml.SelectSingleNode(node)
	}
	sn(node){
		return this.xml.SelectNodes(node)
	}
	__Get(x=""){
		return this.xml.xml
	}
	Get(path,Default){
		return value:=this.ssn(path).text!=""?this.ssn(path).text:Default
	}
	transform(){
		static
		if !IsObject(xsl){
			xsl:=ComObjCreate("MSXML2.DOMDocument")
			style=
			(
			<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
			<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
			<xsl:template match="@*|node()">
			<xsl:copy>
			<xsl:apply-templates select="@*|node()"/>
			<xsl:for-each select="@*">
			<xsl:text></xsl:text>		
			</xsl:for-each>
			</xsl:copy>
			</xsl:template>
			</xsl:stylesheet>
			)
			xsl.loadXML(style),style:=null
		}
		this.xml.transformNodeToObject(xsl,this.xml)
	}
	save(x*){
		if x.1=1
			this.Transform()
		filename:=this.file?this.file:x.1.1,file:=fileopen(filename,"rw","Utf-16")
		if(this.xml.xml==file.read(file.length))
			return
		file.seek(0),file.write(this[]),file.length(file.position)
	}
	remove(rem){
		if !IsObject(rem)
			rem:=this.ssn(rem)
		rem.ParentNode.RemoveChild(rem)
	}
	ea(path){
		list:=[]
		if nodes:=path.nodename
			nodes:=path.SelectNodes("@*")
		else if path.text
			nodes:=this.sn("//*[text()='" path.text "']/@*")
		else if !IsObject(path)
			nodes:=this.sn(path "/@*")
		else
			for a,b in path
				nodes:=this.sn("//*[@" a "='" b "']/@*")
		while,n:=nodes.item(A_Index-1)
			list[n.nodename]:=n.text
		return list
	}
}
ssn(node,path){
	return node.SelectSingleNode(path)
}
sn(node,path){
	return node.SelectNodes(path)
}
AddHotkey(){
	static
	KeyWait,Enter,U
	Gui,1:Default
	Gui,1:TreeView,SysTreeView321
	workspace:=settings.ssn("//*[@tv='" TV_GetSelection() "']")
	if !(InStr(workspace.nodename,"workspace")||workspace.nodename="setting")
		return m("Please select a workspace to add a hotkey to")
	Gui,2:Destroy
	Gui,2:Default
	Gui,Add,Hotkey,w200 vhotkey hwndhotkeyhwnd,% ssn(workspace,"@hotkey").text
	Gui,Add,Edit,w200 vedit gedithotkey
	Gui,Add,Button,gsavehotkey Default,Save Hotkey
	Gui,Show,,Edit Hotkey
	return
	2GuiEscape:
	2GuiClose:
	Gui,2:Destroy
	return
	edithotkey:
	Gui,2:Submit,Nohide
	GuiControl,2:,%hotkeyhwnd%,%edit%
	return
	savehotkey:
	Gui,Submit,Nohide
	if(Hotkey=""&&edit){
		MsgBox,36,Non-Standard Hotkey,This is a non-standard hotkey. Use it?
		IfMsgBox,No
			return
		hotkey:=edit
	}
	workspace.SetAttribute("hotkey",hotkey),PopulateGroups(1)
	Gui,2:Destroy
	return
}
WorkSpaceState(select:=1){
	Gui,1:Default
	Gui,1:TreeView,SysTreeView321
	if TV_GetChild(0){
		for a,b in ["VisFirst","expand","select"]{
			rem:=settings.sn("//*[@" b "='1']")
			while,rr:=rem.Item[A_Index-1]
				rr.RemoveAttribute(b)
		}tv:=0
		while,tv:=TV_GetNext(tv,"F")
			if TV_Get(tv,"E")
				(flan:=settings.ssn("//*[@tv='" tv "']")).SetAttribute("expand",1)
		SendMessage,0x1100+10,5,,,% hwnd(["tv"])
		if ErrorLevel
			settings.ssn("//*[@tv='" ErrorLevel "']").SetAttribute("VisFirst","1")
		settings.ssn("//*[@tv='" TV_GetSelection() "']").SetAttribute("select",1)
	}
}
Move_Windows(){
	movedown:
	Gui,1:Default
	Gui,1:TreeView,SysTreeView321
	current:=settings.ssn("//*[@tv='" TV_GetSelection() "']")
	if(current.nodename!="window"||current.nextsibling.xml="")
		return
	WorkSpaceState(),root:=current.ParentNode
	if next:=current.nextsibling.nextsibling
		root.insertbefore(current,next)
	else
		root.AppendChild(current)
	PopulateGroups(1)
	return
	moveup:
	Gui,1:Default
	Gui,1:TreeView,SysTreeView321
	current:=settings.ssn("//*[@tv='" TV_GetSelection() "']")
	if(current.nodename!="window"||(prev:=current.previoussibling).xml="")
		return
	WorkSpaceState(),root:=current.ParentNode,root.InsertBefore(current,prev),PopulateGroups(1)
	return
}
enter(){
	enter:
	Gui,1:Default
	Gui,1:TreeView,SysTreeView321
	ControlGetFocus,Focus,% hwnd([1])
	if(focus="SysTreeView321"){
		sel:=TV_GetSelection(),current:=settings.ssn("//*[@tv='" sel "']"),ea:=xml.ea(current)
		if InStr(current.nodename,"workspace")
			AddHotkey()
		else if(ea.title~="(Auto Close|Auto Open|Maximize)"){
			current.SetAttribute("value",ea.value?0:1)
		}else if(ea.title="run"){
			file:=ea.run
			SplitPath,file,,dir
			FileSelectFile,newinfo,,%dir%,Please select the program to run: Escape for Folder Select
			if(FileExist(newinfo)=""||newinfo="")
				return
			current.SetAttribute("value",newinfo)
		}else if(ea.title="position")
			return updatepos(current),PopulateGroups()
		else if(current.nodename="window"){
			InputBox,newinfo,New Title,Enter a new title,,,,,,,,% ssn(current,"@title").text
			if(ErrorLevel||newinfo="")
				return
			current.SetAttribute("title",newinfo)
		}else if(current.nodename="setting"){
			AddHotkey()
		}
		PopulateGroups(1)
	}
	return
}
tv(check:=0){
	tv:
	Gui,1:TreeView,SysTreeView321
	Gui,1:Default
	if(A_GuiEvent="doubleclick"){
		node:=settings.ssn("//*[@tv='" A_EventInfo "']"),ea:=xml.ea(node)
		if(ea.title="Run"){
			InputBox,run,New Run Value,Enter a file path/folder to run,,,,,,,,% ea.value
			if(ErrorLevel||run="")
				return
			return node.SetAttribute("value",run),PopulateGroups(1)
		}if(ea.title="position"){
			InputBox,newpos,New Position,Edit the windows position,,,,,,,,% ea.value
			if(ErrorLevel||newpos="")
				return
			return node.SetAttribute("value",newpos),PopulateGroups(1)
		}
	}
	else if(A_GuiEvent="RightClick"){
		node:=settings.ssn("//*[@tv='" A_EventInfo "']"),ea:=xml.ea(node)
		if(node.nodename="window"){
			pos:=[]
			if !WinExist(ea.title){
				run:=ssn(node,"item[@title='Run']/@value").text
				SplitPath,run,file,dir
				if !file
					Run,%dir%
				else
					Run,%file%,%dir%
			}
			WinGet,max,MinMax,% ea.title
			if(max=-1)
				WinRestore,% ea.title
			WinActivate,% ea.title
			SysGet,count,MonitorCount
			position:=(ssn(node,"*[@title='Monitor Count'][@value='" count "']/position/@value").text)
			position:=position?position:ssn(node,"descendant::position/@value").text
			for a,b in StrSplit(position," ")
				pos[SubStr(b,1,1)]:=SubStr(b,2)
			if(getvalue(node,"Maximize")){
				WinRestore,% ea.title
				WinMaximize,% ea.title
			}else
				WinMove,% ea.title,,% pos.x,% pos.y,% pos.w,% pos.h
			WinActivate,% hwnd([1])
		}if(ea.title="run"){
			FileSelectFolder,dir,,,Select a folder to open
			if ErrorLevel
				return
			node.SetAttribute("value",dir),PopulateGroups(1)
		}
	}
	return
}
update(){
	update:
	if FileExist("gui.ahk")
		return
	URLDownloadToFile,http://files.maestrith.com/Workspace/Workspace.ahk,temp.ahk
	FileRead,temp,temp.ahk
	if InStr(temp,"Find This"){
		settings.save(1)
		SplitPath,A_ScriptFullPath,file,dir,,name
		FileMove,%A_ScriptName%,%dir%\%name% %A_Now%.ahk,1
		FileMove,temp.ahk,%A_ScriptName%,1
		Reload
		ExitApp
	}
	return
}
Hotkey(){
	static
	hotkey:
	current:=settings.ssn("//*[@hotkey='" A_ThisHotkey "']")
	if(current.nodename="workspaces"){
		if Visible
			return t(),visible:=0
		list:=settings.sn("//workspace[@hotkey]"),keylist:=""
		while,ll:=list.item[A_Index-1],ea:=xml.ea(ll)
			keylist.=ea.title " = " ea.hotkey "`n"
		Visible:=1
		return t(keylist)
	}else if(current.nodename="setting"),ea:=xml.ea(current){
		if(ea.name="Hide/Show GUI"){
			GuiClose:
			WinGet,max,minmax,% hwnd([1])
			if max=0
				WinHide,% hwnd([1])
			if (max=""){
				WinShow,% hwnd([1])
				WinActivate,% hwnd([1])
			}
			return
		}else if(ea.name="Toggle Current Workspace"){
			current:=settings.ssn("//workspace[@current]")
			if !current
				settings.ssn("//workspace").SetAttribute("current",1)
			else{
				if(next:=current.nextsibling){
					current.RemoveAttribute("current")
					next.SetAttribute("current",1)
				}else{
					current.RemoveAttribute("current")
					settings.ssn("//workspace").SetAttribute("current",1)
				}
			}
			current:=settings.ssn("//workspace[@current]")
			Restore(sn(current,"descendant::window"),1,1)
		}
	}
	windows:=settings.sn("//*[@hotkey='" A_ThisHotkey "']/descendant::window"),minimized:=""
	Gui,1:Default
	Gui,1:TreeView,SysTreeView321
	TV_GetText(workspace,TV_GetSelection())
	while,ww:=windows.item[A_Index-1],ea:=xml.ea(ww){
		if(WinActive(ea.title " ahk_class " getvalue(ww,"Class"))){
			position:=(ssn(ww,"*[@title='Monitor Count'][@value='" count "']/position/@value").text),position:=position?position:ssn(ww,"descendant::position/@value").text
			WinGetPos,x,y,w,h,% ea.title " ahk_class " getvalue(ww,"Class")
			for a,b in {x:x,y:y,w:w,h:h}{
				RegExMatch(position,"Oi)" a "(\d+)",found)
				if(found.1!=b){
					minimized:=1
					Goto,hkbottom
				}
			}
			minimized:=0
			goto,hkbottom
		}
	}Minimized:=1
	hkbottom:
	Restore(windows,minimized)
	return
}
UriDecode(Uri) {
	Pos := 1
	While Pos := RegExMatch(Uri, "i)(%[\da-f]{2})+", Code, Pos)
	{
		VarSetCapacity(Var, StrLen(Code) // 3, 0), Code := SubStr(Code,2)
		Loop, Parse, Code, `%
			NumPut("0x" A_LoopField, Var, A_Index-1, "UChar")
		Decoded := StrGet(&Var, "UTF-8")
		Uri := SubStr(Uri, 1, Pos-1) . Decoded . SubStr(Uri, Pos+StrLen(Code)+1)
		Pos += StrLen(Decoded)+1
	}
	Return, Uri
}
getvalue(node,value){
	return ssn(node,"descendant::*[@title='" value "']/@value").text
}
Restore(windows,minimized,skipwait:=0){
	while,ww:=windows.item[windows.length-(A_Index)],ea:=xml.ea(ww){
		if(minimized){
			if (WinExist(ea.title)=0){
				if !getvalue(ww,"Auto Open")
					Continue
				file:=getvalue(ww,"Run")
				SplitPath,file,filename,dir
				if !filename
					Run,%dir%
				else
					Run,%filename%,%dir%
				WinWait,% ea.title,,1
			}
			WinActivate,% ea.title " ahk_class " class:=getvalue(ww,"Class")
			pos:=[]
			SysGet,count,MonitorCount
			position:=(ssn(ww,"*[@title='Monitor Count'][@value='" count "']/position/@value").text),position:=position?position:ssn(ww,"descendant::position/@value").text
			for a,b in StrSplit(position," ")
				pos[SubStr(b,1,1)]:=SubStr(b,2)
			if(getvalue(ww,"Maximize")){
				WinWaitActive,% ea.title
				WinMaximize,% ea.title
			}else
				WinMove,% ea.title,,% pos.x,% pos.y,% pos.w,% pos.h
		}else{
			WinGet,list,list,% "ahk_class" ssn(ww,"*[@title='Class']/@value").text
			match:=[]
			Loop,%list%
			{
				WinGetTitle,title,% "ahk_id" list%A_Index%
				if InStr(title,ea.title)
					match[title]:=list%A_Index%
			}
			for a,b in match{
				if(getvalue(ww,"Auto Close"))
					WinClose,ahk_id %b%
				else
					WinMinimize,ahk_id %b%
			}
		}
	}t()
}
Update_Positions(){
	updatepos:
	wl:=settings.sn("//window")
	while,ww:=wl.item[A_Index-1]{
		win:=ssn(ww,"@title").text
		WinGet,max,MinMax,%win%
		if (max=0)
			updatepos(ww)
	}PopulateGroups(1)
	return
}
updatepos(current){
	SysGet,count,MonitorCount
	parent:=ssn(current,"ancestor-or-self::window")
	WinGetPos,x,y,w,h,% ssn(parent,"@title").text
	if !position:=ssn(parent,"*[@title='Monitor Count'][@value='" count "']")
		position:=settings.under({under:parent,node:"monitor",att:{title:"Monitor Count",value:count}})
	if !pos:=ssn(position,"position")
		pos:=settings.under({under:position,node:"position",att:{title:"Position"}})
	if(x!=""&&y!=""&&w!=""&&h!=""){
		pos.SetAttribute("value","x" x " y" y " w" w " h" h)
	}else{
		InputBox,newinfo,New Position,Enter a new position for this window,,,,,,,,% ea.position
		if(ErrorLevel||newinfo="")
			return m("Please enter a value for this window")
		pos.SetAttribute("value",newinfo)
	}
	WorkSpaceState(1),pos.SetAttribute("select",1)
}
Help(){
	help=
(
Adding Workspaces: Alt+C

	Click Create Workspace

Adding Windows To A Workspace: Alt+A

	Highlight a workspace, then Click Add Windows To Selected Workspace

Update Open Windows Positions: Alt+U

	This will update the positions to all of the visible windows

Contract All Workspaces: Alt+T

	Contracts everything in the treeview except for the Workspaces


Adding Hotkeys:

	You can add hotkeys to the following by selecting the node and pressing Enter

	The main Workspaces node:
		This hotkey will display/hide the main hotkeys for your workspaces

	Any Workspace node:
		This will associate that workspace with a hotkey so when you press
			that hotkey it will hide/show those windows

	Settings:

		Hide/Show GUI:
			This will hide/show the main gui
	
		Toggle Current Workspace:
			This will bring the next workspace in the list to the forground

	Updating Individual Window Positions:

		Select the position attribute for the window and press enter
)
	Gui,2:Destroy
	Gui,2:Add,Text,w500 -Wrap,%help%
	Gui,2:Show,,Help
}
chucknp

Re: AHK Workspaces - Window Management Script by Maestrith

03 May 2016, 10:38

Very useful script - thanks! After a little more testing, I'm about to replace the section of my global script that starts/positions apps.

Regards,
Chuck
5nwYHrhNRY
Posts: 10
Joined: 01 May 2018, 07:18

Re: AHK Workspaces - Window Management Script by Maestrith

15 Oct 2018, 16:42

Error: Parameter #1 invalid.
Specifically: 1Default (Line 17)

Any ideas?

Return to “Scripts and Functions”

Who is online

Users browsing this forum: blue83 and 45 guests