Code: Select all
menu:
page_size: 5
punctuator:
full_shape:
" ": {commit: " "}
"!": {commit: "!"}
"\"": {pair: ["“", "”"]}
"#": ["#", "⌘"]
"$": ["¥", "$", "€", "£", "¥", "¢", "¤"]
"%": ["%", "°", "℃"]
"&": "&"
"'": {pair: ["‘", "’"]}
"(": "("
")": ")"
"*": ["*", "·", "・", "×", "※", "❂"]
"+": "+"
",": {commit: ","}
"-": "-"
.: {commit: "。"}
"/": ["/", "÷"]
":": {commit: ":"}
";": {commit: ";"}
"<": ["《", "〈", "«", "‹"]
"=": "="
">": ["》", "〉", "»", "›"]
"?": {commit: "?"}
"@": ["@", "☯"]
"[": ["「", "【", "〔", "["]
"\\": ["、", "\"]
"]": ["」", "】", "〕", "]"]
"^": {commit: "……"}
_: "——"
"`": "`"
"{": ["『", "〖", "{"]
"|": ["·", "|", "§", "¦"]
"}": ["』", "〗", "}"]
"~": "~"
recognizer:
patterns:
email: "^[A-Za-z][-_.0-9A-Za-z]*@.*$"
Code: Select all
Yaml(YamlText,IsFile=1,YamlObj=0){ ; Version 1.0.0.17 http://www.autohotkey.com/forum/viewtopic.php?t=70559
static
static base:={Dump:"Yaml_Dump",Save:"Yaml_Save",Add:"Yaml_Add",Merge:"Yaml_Merge",__Delete:"__Delete",_Insert:"_Insert",_Remove:"_Remove",_GetCapacity:"_GetCapacity",_SetCapacity:"_SetCapacity",_GetAddress:"_GetAddress",_MaxIndex:"_MaxIndex",_MinIndex:"_MinIndex",_NewEnum:"_NewEnum",_HasKey:"_HasKey",_Clone:"_Clone",Insert:"Insert",Remove:"Remove",GetCapacity:"GetCapacity",SetCapacity:"SetCapacity",GetAddress:"GetAddress",MaxIndex:"MaxIndex",MinIndex:"MinIndex",NewEnum:"NewEnum",HasKey:"HasKey",Clone:"Clone",base:{__Call:"Yaml_Call"}}
static BackupVars:="LVL,SEQ,KEY,SCA,TYP,VAL,CMT,LFL,CNT",IncompleteSeqMap
local maxLVL:=0,LastContObj:=0,LastContKEY:=0,LinesAdded:=0,_LVLChanged:=0,_LVL,_SEQ,_KEY,_SCA,_TYP,_VAL,_CMT,_LFL,_CNT,_NXT,__LVL,__SEQ,__KEY,__SCA,__TYP,__VAL,__CMT,__LFL,__CNT,__NXT
AutoTrim % ((AutoTrim:=A_AutoTrim)="On")?"Off":"Off"
LVL0:=pYaml:=YamlObj?YamlObj:Object("base",base),__LVL:=0,__LVL0:=0
If IsFile
FileRead,YamlText,%YamlText%
Loop,Parse,YamlText,`n,`r
{
If (!_CNT && (A_LoopField=""||RegExMatch(A_LoopField,"^\s+$"))){ ;&&__KEY=""&&__SEQ="")){
If ((OBJ:=LVL%__LVL%[""].MaxIndex())&&IsObject(LVL%__LVL%["",OBJ])&&__SEQ){
If (__KEY!="")
Yaml_Continue(LastContObj:=LVL%__LVL%["",Obj],LastContKEY:=__key,"",__SCA)
else Yaml_Continue(LastContObj:=LVL%__LVL%[""],LastContKEY:=Obj,"",__SCA,__SEQ)
} else If (__SEQ && OBJ){
Yaml_Continue(LastContObj:=LVL%__LVL%[""],LastContKEY:=Obj,"",__SCA,__SEQ)
Yaml_Continue(LastContObj:=LVL%__LVL%[""],LastContKEY:=OBJ,"",__SCA,__SEQ)
} else If (OBJ){
Yaml_Continue(LastContObj:=LVL%__LVL%[""],LastContKEY:=OBJ,"",__SCA,1)
} else if (__KEY!="")
Yaml_Continue(LastContObj:=LVL%__LVL%,LastContKEY:=__KEY,"",__SCA)
else LinesAdded--
LinesAdded++
Continue
} else If (!_CNT && LastContObj
&& ( RegExMatch(A_LoopField,"^(---)?\s*?(-\s)?("".+""\s*:\s|'.+'\s*:\s|[^:""'\{\[]+\s*:\s)")
|| RegExMatch(A_LoopField,"^(---)|\s*(-\s)") )){
If !__SCA
LastContObj[LastContKEY]:=SubStr(LastContObj[LastContKEY],1,-1*LinesAdded)
LastContObj:=0,LastContKEY:=0,LinesAdded:=0
}
If InStr(A_LoopField,"#"){
If (RegexMatch(A_LoopField,"^\s*#.*") || InStr(A_LoopField,"%YAML")=1) ;Comments only, do not parse
continue
else if Yaml_IsQuoted(LTrim(A_LoopField,"- ")) || RegExMatch(A_LoopField,"(---)?\s*?(-\s)?("".+""\s*:\s|'.+'\s*:\s|[^:""'\{\[]+\s*:\s)\s*([\|\>][+-]?)?\s*(!!\w+\s)?\s*("".+|'.+)$")&&!RegExMatch(A_LoopField,"[^\\]""\s+#")
LoopField:=A_LoopField
else if RegExMatch(A_LoopField,"\s+#.*$","",RegExMatch(A_LoopField,"(---)?\s*?(-\s)?("".+""\s*:\s|'.+'\s*:\s|[^:""'\{\[]+\s*:\s)?\s*([\|\>][+-]?)?\s*(!!\w+\s)?\s*("".+""|'.+')?\K")-1)
LoopField:=SubStr(A_LoopField,1,RegExMatch(A_LoopField,"\s+#.*$","",RegExMatch(A_LoopField,"(---)?\s*?(-\s)?("".+""\s*:\s|'.+'\s*:\s|[^:""'\{\[]+\s*:\s)?\s*([\|\>][+-]?)?\s*(!!\w+\s)?\s*("".+""|'.+')?\K")-1)-1)
else LoopField:=A_LoopField
} else LoopField:=A_LoopField
If _CNT {
If Yaml_IsSeqMap(RegExReplace(IncompleteSeqMap LoopField,"^(\s+)?(-\s)?("".+""\s*:\s|'.+'\s*:\s|[^:""'\{\[]+\s*:\s)?"))
LoopField:=IncompleteSeqMap LoopField,_CNT:=0,IncompleteSeqMap:=""
else {
IncompleteSeqMap.=LoopField
continue
}
}
If (LoopField="---"){
Loop % (maxLVL)
LVL%A_Index%:=""
Loop,Parse,BackupVars,`,
__%A_LoopField%:="",__%A_LoopField%0:=""
Loop,Parse,BackupVars,`,
Loop % maxLVL
__%A_LoopField%%A_Index%:=""
maxLVL:=0
__LVL:=0,__LVL0:=0
If !IsObject(pYaml[""])
pYaml[""]:=LVL0:=Object("base",base)
pYaml[""].Insert(LVL0:=Object("base",base))
Continue
} else if (LoopField="..."){
LVL0:=pYaml
Loop % maxLVL
LVL%A_Index%:=""
Loop,Parse,BackupVars,`,
__%A_LoopField%:="",__%A_LoopField%0:=""
Loop,Parse,BackupVars,`,
Loop % maxLVL
__%A_LoopField%%A_Index%:=""
maxLVL:=0
__LVL:=0,__LVL0:=0
Continue
}
If (SubStr(LoopField,0)=":")
LoopField.=A_Space ; add space to force RegEx to match even if the value and space after collon is missing e.g. Object:`n objects item
RegExMatch(LoopField,"S)^(?<LVL>\s+)?(?<SEQ>-\s)?(?<KEY>"".+""\s*:\s|'.+'\s*:\s|[^:""'\{\[]+\s*:\s)?\s*(?<SCA>[\|\>][+-]?)?\s*(?<TYP>!!\w+\s)?\s*(?<VAL>"".+""|'.+'|.+)?\s*$",_)
If _KEY ;cut off (:)
StringTrimRight,_KEY,_KEY,2
_KEY:=Yaml_UnQuoteIfNeed(_KEY)
If IsVal:=Yaml_IsQuoted(_VAL)
_VAL:=Yaml_UnQuoteIfNeed(_VAL)
;determine current level
_LVL:=Yaml_S2I(_LVL)
If _LVL-__LVL>1||(_LVL>__LVL&&_LVLChanged) ;&&!(__SEQ&&__KEY!=""&&_KEY!="")) ; (__SEQ?2:1)
{
Loop % (_LVLChanged?_LVL-_LVLChanged:_LVL-__LVL-1)
LoopField:=SubStr(LoopField,SubStr(LoopField,1,1)=A_Tab?1:2)
_LVL:=_LVLChanged?_LVLChanged:__LVL+1,_LVLChanged:=_LVLChanged?_LVLChanged:_LVL ;__LVL%_LVL%:=__LVL%_NXT% ; (__SEQ?2:1)
} else if _LVLChanged
_LVL:=_LVLChanged
else _LVLChanged:=0
If (maxLVL<_LVL)
maxLVL:=_LVL+(_SEQ?1:0)
; Cut off the leading tabs/spaces conform _LVL
SubStr:=0,Tabs:=0
Loop,Parse,LoopField
If (_LVL*2=SubStr || !SubStr:=SubStr+(A_LoopField=A_Tab?2:1)), Tabs:=Tabs+(A_LoopField=A_Tab?1:0)
break
_LFL:=SubStr(LoopField,SubStr-Tabs+1+(_SEQ?2:0))
_LFL:=Yaml_UnQuoteIfNeed(_LFL)
_NXT:=_LVL+1 ;next indentation level
__NXT:=_NXT+1
_PRV:=_LVL=0?0:_LVL-1
Loop,Parse,BackupVars,`,
__%A_LoopField%:=__%A_LoopField%%_PRV%
If RegExMatch(_LFL,"^-\s*$"){
_SEQ:="-",_KEY:="",_VAL:=""
}
If (!IsVal && !_CNT && (_CNT:=Yaml_Incomplete(Trim(_LFL))||Yaml_Incomplete(Trim(_VAL)))){
IncompleteSeqMap:=LoopField
continue
}
If (_LVL<__LVL){ ;Reset Objects and Backup vars
Loop % (maxLVL)
If (A_Index>_LVL){
Loop,Parse,BackupVars,`,
__%A_LoopField%%maxLVL%:=""
LVL%A_Index%:="",maxLVL:=maxLVL-1
}
If (_LVL=0 && !__LVL:=__LVL0:=0)
Loop,Parse,BackupVars,`,
__%A_LoopField%:="",__%A_LoopField%0:=""
}
If (_SEQ&&_LVL>__LVL&&(__VAL!=""||__SCA))
_SEQ:="",_KEY:="",_VAL:="",_LFL:="- " _LFL
If (__CNT)||(_LVL>__LVL&&(__KEY!=""&&_KEY="")&&(__VAL!=""||__SCA))||(__SEQ&&__SCA)
_KEY:="",_VAL:=""
If (__CNT||(_LVL>__LVL&&(__KEY!=""||(__SEQ&&(__LFL||__SCA)&&!Yaml_IsSeqMap(__LFL)))&&!(_SEQ||_KEY!=""))){
If ((OBJ:=LVL%__LVL%[""].MaxIndex())&&IsObject(LVL%__LVL%["",OBJ])&&__SEQ){
If __KEY!=
Yaml_Continue(LVL%__LVL%["",Obj],__key,_LFL,__SCA),__CNT:=Yaml_SeqMap(LVL%__LVL%["",OBJ],__KEY,LVL%__LVL%["",OBJ,__KEY])?"":__CNT
else Yaml_Continue(LVL%__LVL%[""],Obj,_LFL,__SCA,__SEQ),__CNT:=Yaml_SeqMap(LVL%__LVL%[""],OBJ,LVL%__LVL%["",OBJ],__SEQ)?"":__CNT
} else If (__SEQ && OBJ){
Yaml_Continue(LVL%__LVL%[""],Obj,_LFL,__SCA,__SEQ)
__CNT:=Yaml_SeqMap(LVL%__LVL%[""],OBJ,LVL%__LVL%["",OBJ],__SEQ)?"":__CNT
} else If (OBJ && __KEY=""){
Yaml_Continue(LVL%__LVL%[""],OBJ,_LFL,__SCA,1)
__CNT:=Yaml_SeqMap(LVL%__LVL%[""],OBJ,LVL%__LVL%["",OBJ],1)?"":__CNT
} else {
Yaml_Continue(LVL%__LVL%,__KEY,_LFL,__SCA)
__CNT:=Yaml_SeqMap(LVL%__LVL%,__KEY,LVL%__LVL%[__KEY])?"":__CNT
}
Continue
}
;Create sequence or map
If (__SEQ&&(_LVL>__LVL)&&_KEY!=""&&__KEY!=""){
OBJ:=LVL%__LVL%[""].MaxIndex()
If _SEQ {
If !Yaml_SeqMap(LVL%_LVL%["",OBJ,__KEY,""],_KEY,_VAL){
If !IsObject(LVL%__LVL%["",OBJ,__KEY,""])
LVL%__LVL%["",OBJ,__KEY,""]:={base:base}
LVL%__LVL%["",OBJ,__KEY,""].Insert({(_KEY):_VAL!=""?_VAL:(LVL%_NXT%:={base:base}),base:base})
}
} else If !Yaml_SeqMap(LVL%_LVL%["",OBJ],_KEY,_VAL){
LVL%__LVL%["",OBJ,_KEY]:=_VAL!=""?_VAL:(LVL%_NXT%:={base:base})
}
If _VAL!=
continue
} else If (_SEQ){
If !IsObject(LVL%_LVL%[""])
LVL%_LVL%[""]:=Object("base",base)
While (SubStr(_LFL,1,2)="- "){
_LFL:=SubStr(_LFL,3),_KEY:=(_KEY!="")?_LFL:=SubStr(_KEY,3):_KEY,LVL%_LVL%[""].Insert(LVL%_NXT%:=Object("",Object("base",base),"base",base)),_LVL:=_LVL+1,_NXT:=_NXT+1,__NXT:=_NXT+1,_PRV:=_LVL-1,maxLVL:=(maxLVL<_LVL)?_LVL:maxLVL
Loop,Parse,BackupVars,`,
__%A_LoopField%:=_%A_LoopField%
,__%A_LoopField%%_PRV%:=_%A_LoopField%
}
If (_KEY="" && _VAL="" && !IsVal){
If !Yaml_SeqMap(LVL%_LVL%[""],"",_LFL)
LVL%_LVL%[""].Insert(LVL%_NXT%:=Object("base",base))
} else If (_KEY!="") {
LVL%_LVL%[""].Insert(LVL%__NXT%:=Object(_KEY,LVL%_NXT%:=Object("base",base),"base",base))
If !Yaml_SeqMap(LVL%__NXT%,_KEY,_VAL){
LVL%_LVL%[""].Remove()
LVL%_LVL%[""].Insert(LVL%__NXT%:=Object(_KEY,(_VAL!=""||IsVal)?_VAL:LVL%_NXT%:=Object("base",base),"base",base))
}
} else {
If !Yaml_SeqMap(LVL%_LVL%[""],"",_LFL)
LVL%_LVL%[""].Insert(_LFL)
}
If !LVL%_LVL%[""].MaxIndex()
LVL%_LVL%.Remove("")
} else if (_KEY!=""){
If (__SEQ && _LVL>__LVL) {
If (OBJ:=LVL%_PRV%[""].MaxIndex())&&IsObject(LVL%_PRV%["",OBJ]){
If !Yaml_SeqMap(LVL%_PRV%["",OBJ],_KEY,_VAL)
LVL%_PRV%["",OBJ,_KEY]:=(_VAL!=""||IsVal)?_VAL:(LVL%_NXT%:=Object("base",base))
} else {
LVL%_PRV%[""].Insert(Object(_KEY,(_VAL!=""||IsVal)?_VAL:(LVL%_NXT%:=Object("base",base)),"base",base))
Yaml_SeqMap(LVL%_PRV%["",OBJ?OBJ+1:1],_KEY,_VAL)
}
} else
If !Yaml_SeqMap(LVL%_LVL%,_KEY,_VAL)
LVL%_LVL%[_KEY]:=_VAL!=""?_VAL:(LVL%_NXT%:=Object("base",base))
} else if (_LVL>__LVL && (__KEY!="")) {
If (__VAL!="" || __SCA){
Yaml_Continue(LVL%__LVL%,__KEY,_LFL,__SCA)
Yaml_SeqMap(LVL%__LVL%,__KEY,LVL%__LVL%[__KEY])
Continue
} else {
If !Yaml_SeqMap(LVL%__LVL%[__KEY],_KEY,_VAL) ;!!! no Scalar???
LVL%__LVL%[__KEY,_KEY]:=_VAL
Continue
}
} else {
If (_LVL>__LVL&&(OBJ:=LVL%__LVL%[""].MaxIndex())&&IsObject(LVL%__LVL%["",OBJ])&&__SEQ){
If __CNT
Yaml_Continue(LVL%__LVL%[""],LVL%__LVL%[""].MaxIndex(),_LFL,__SCA,1)
If (__CNT:=Yaml_SeqMap(LVL%__LVL%[""],"",_LFL)?"":1)
LVL%__LVL%[""].Insert(_LFL)
} else {
If !IsObject(LVL%_LVL%[""])
LVL%_LVL%[""]:=Object("base",base)
If __CNT
Yaml_Continue(LVL%__LVL%[""],LVL%__LVL%[""].MaxIndex(),_LFL,__SCA,1)
If (__CNT:=Yaml_SeqMap(LVL%_LVL%[""],"",_LFL)?"":1)
LVL%_LVL%[""].Insert(_LFL)
}
Continue
}
Loop,Parse,BackupVars,`,
__%A_LoopField%:=_%A_LoopField%
,__%A_LoopField%%_LVL%:=_%A_LoopField%
}
If (LastContObj && !__SCA)
LastContObj[LastContKEY]:=SubStr(LastContObj[LastContKEY],1,-1*LinesAdded)
AutoTrim %AutoTrim%
Loop,Parse,BackupVars,`,
If !(__%A_LoopField%:="")
Loop % maxLVL
__%A_LoopField%%A_Index%:=""
Return pYaml,pYaml.base:=base
}
Yaml_Save(obj,file,level=""){
FileMove,% file,% file ".bakupyml",1
FileAppend,% obj.Dump(),% file
If !ErrorLevel
FileDelete,% file ".bakupyml"
else {
FileMove,% file ".bakupyml",% file
MsgBox,0, Error creating file, old file was restored.
}
}
Yaml_Call(NotSupported,f,p*){
If (p.MaxIndex()>1){
Loop % p.MaxIndex()
If A_Index>1
f:=f[""][p[A_Index-1]]
}
Return (!p.MaxIndex()?f[""].MaxIndex():f[""][p[p.MaxIndex()]])
}
Yaml_Merge(obj,merge){
for k,v in merge
{
If IsObject(v){
If obj.HasKey(k){
If IsObject(obj[k])
Yaml_Merge(obj[k],v)
else obj[k]:=v
} else obj[k]:=v
} else obj[k]:=v
}
}
Yaml_Add(O,Yaml="",IsFile=0){
static base:={Dump:"Yaml_Dump",Save:"Yaml_Save",Add:"Yaml_Add",Merge:"Yaml_Merge",__Delete:"__Delete",_Insert:"_Insert",_Remove:"_Remove",_GetCapacity:"_GetCapacity",_SetCapacity:"_SetCapacity",_GetAddress:"_GetAddress",_MaxIndex:"_MaxIndex",_MinIndex:"_MinIndex",_NewEnum:"_NewEnum",_HasKey:"_HasKey",_Clone:"_Clone",Insert:"Insert",Remove:"Remove",GetCapacity:"GetCapacity",SetCapacity:"SetCapacity",GetAddress:"GetAddress",MaxIndex:"MaxIndex",MinIndex:"MinIndex",NewEnum:"NewEnum",HasKey:"HasKey",Clone:"Clone",base:{__Call:"Yaml_Call"}}
If Yaml_IsSeqMap(Trim(Yaml)){
If !IsObject(O[""])
O[""]:=Object("base",base)
Yaml_SeqMap(O[""],"",Yaml)
} else Yaml(Yaml,IsFile,O)
}
Yaml_Dump(O,J="",R=0,Q=0){
static M1:="{",M2:="}",S1:="[",S2:="]",N:="`n",C:=", ",S:="- ",E:="",K:=": "
local dump:="",M,MX,F,I,key,value
If (J=0&&!R)
dump.= S1
for key in O
M:=A_Index
If IsObject(O[""]){
M--
for key in O[""]
MX:=A_Index
If IsObject(O[""][""])
MX--
If O[""].MaxIndex()
for key, value in O[""]
{
If key=
continue
I++
F:=IsObject(value)?(IsObject(value[""])?"S":"M"):E
If (J!=""&&J<=R){
dump.=(F?(%F%1 Yaml_Dump(value,J,R+1,F) %F%2):Yaml_EscIfNeed(value)) (I=MX&&!M?E:C) ;(Q="S"&&I=1?S1:E)(Q="S"&&I=MX?S2:E)
} else if F,dump:=dump N Yaml_I2S(R) S
dump.= (J!=""&&J<=(R+1)?%F%1:E) Yaml_Dump(value,J,R+1,F) (J!=""&&J<=(R+1)?%F%2:E)
else {
; If RegexMatch(value,"[\x{007F}-\x{FFFF}""\{\[']|:\s|\s#")
dump .= Yaml_EscIfNeed(value)
; else {
; value:= (value=""?"''":RegExReplace(RegExReplace(Value,"m)^(.*[\r\n].*)$","|" (SubStr(value,-1)="`n`n"?"+":SubStr(value,0)=N?"":"-") "`n$1"),"ms)(*ANYCRLF)\R",N Yaml_I2S(R+1)))
; StringReplace,value,value,% N Yaml_I2S(R+1) N Yaml_I2S(R+1),% N Yaml_I2S(R+1),A
; dump.=value
; }
}
}
}
I=0
for key, value in O
{
If key=
continue
I++
F:=IsObject(value)?(IsObject(value[""])?"S":"M"):E
If (J=0&&!R)
dump.= M1
If (J!=""&&J<=R){
dump.=(Q="S"&&I=1?M1:E) Yaml_EscIfNeed(key) K
dump.=F?(%F%1 Yaml_Dump(value,J,R+1,F) %F%2):Yaml_EscIfNeed(value)
dump.=(Q="S"&&I=M?M2:E) (J!=0||R?(I=M?E:C):E)
} else if F,dump:=dump N Yaml_I2S(R) Yaml_EscIfNeed(key) K
dump.= (J!=""&&J<=(R+1)?%F%1:E) Yaml_Dump(value,J,R+1,F) (J!=""&&J<=(R+1)?%F%2:E)
else {
; If RegexMatch(value,"[\x{007F}-\x{FFFF}""\{\['\t]|:\s|\s#")
dump .= Yaml_EscIfNeed(value)
; else {
; value:= (value=""?"''":RegExReplace(RegExReplace(Value,"m)^(.*[\r\n].*)$","|" (SubStr(value,-1)="`n`n"?"+":SubStr(value,0)="`n"?"":"-") "`n$1"),"ms)(*ANYCRLF)\R","`n" Yaml_I2S(R+1)))
; StringReplace,value,value,% "`n" Yaml_I2S(R+1) "`n" Yaml_I2S(R+1),% "`n" Yaml_I2S(R+1),A
; dump.= value
; }
}
If (J=0&&!R){
dump.=M2 (I<M?C:E)
}
}
If (J=0&&!R)
dump.=S2
If (R=0)
dump:=RegExReplace(dump,"^\R+")
Return dump
}
Yaml_UniChar( string ) {
static a:="`a",b:="`b",t:="`t",n:="`n",v:="`v",f:="`f",r:="`r",e:=Chr(0x1B)
Loop,Parse,string,\
{
If (A_Index=1){
var.=A_LoopField
continue
} else If lastempty {
var.="\" A_LoopField
lastempty:=0
Continue
} else if (A_LoopField=""){
lastempty:=1
Continue
}
If InStr("ux",SubStr(A_LoopField,1,1))
str:=SubStr(A_LoopField,1,RegExMatch(A_LoopField,"^[ux]?([\dA-F]{4})?([\dA-F]{2})?\K")-1)
else
str:=SubStr(A_LoopField,1,1)
If (str=="N")
str:="\x85"
else if (str=="P")
str:="\x2029"
else if (str=0)
str:="\x0"
else if (str=="L")
str:="\x2028"
else if (str=="_")
str:="\xA0"
If RegexMatch(str,"i)^[ux][\da-f]+$")
var.=Chr(Abs("0x" SubStr(str,2)))
else If str in a,b,t,n,v,f,r,e
var.=%str%
else var.=str
If InStr("ux",SubStr(A_LoopField,1,1))
var.=SubStr(A_LoopField,RegExMatch(A_LoopField,"^[ux]?([\dA-F]{4})?([\dA-F]{2})?\K"))
else var.=SubStr(A_LoopField,2)
}
return var
}
Yaml_CharUni( string ) {
static ascii:={"\":"\","`a": "a","`b": "b","`t": "t","`n": "n","`v": "v","`f": "f","`r": "r",Chr(0x1B): "e","""": """",Chr(0x85): "N",Chr(0x2029): "P",Chr(0x2028): "L","": "0",Chr(0xA0): "_"}
If !RegexMatch(string,"[\x{007F}-\x{FFFF}]"){
Loop,Parse,string
{
If ascii[A_LoopField]
var.="\" ascii[A_LoopField]
else
var.=A_LoopField
}
return var
}
format:=A_FormatInteger
SetFormat,Integer,H
Loop,Parse,string
{
If ascii[A_LoopField]
var.="\" ascii[A_LoopField]
else if Asc(A_LoopField)<128
var.=A_LoopField
else {
str:=SubStr(Asc(A_LoopField),3)
var.="\u" (StrLen(str)<2?"000":StrLen(str)<3?"00":StrLen(str)<4?"0":"") str
}
}
SetFormat,Integer,%Format%
return var
}
Yaml_EscIfNeed(s){
If (s="")
return "''"
else If RegExMatch(s,"m)[\{\[""'\r\n]|:\s|,\s|\s#")||RegExMatch(s,"^[\s#\\\-:>]")||RegExMatch(s,"m)\s$")||RegExMatch(s,"m)[\x{7F}-\x{7FFFFFFF}]")
return ("""" . Yaml_CharUni(s) . """")
else return s
}
Yaml_IsQuoted(ByRef s){
return InStr(".''."""".","." SubStr(Trim(s),1,1) SubStr(Trim(s),0) ".")?1:0
}
Yaml_UnQuoteIfNeed(s){
s:=Trim(s)
If !(SubStr(s,1,1)=""""&&SubStr(s,0)="""")
return (SubStr(s,1,1)="'"&&SubStr(s,0)="'")?SubStr(s,2,StrLen(s)-2):s
else return Yaml_UniChar(SubStr(s,2,StrLen(s)-2))
}
Yaml_S2I(str){
local idx:=0
Loop,Parse,str
If (A_LoopField=A_Tab)
idx++
else if !Mod(A_index,2)
idx++
Return idx
}
Yaml_I2S(idx){
Loop % idx
str .= " "
Return str
}
Yaml_Continue(Obj,key,value,scalar="",isval=0){
If !IsObject(isObj:=obj[key])
v:=IsObject(isObj)?"":isObj
;~ v:=isObj
If scalar {
StringTrimLeft,scaopt,scalar,1
scalar:=Asc(scalar)=124?"`n":" "
} else scalar:=" ",scaopt:="-"
temp := (value=""?"`n":(SubStr(v,0)="`n"&&scalar="`n"?"":(v=""?"":scalar))) value (scaopt!="-"?(v&&value=""?"`n":""):"")
obj[key]:=Yaml_UnQuoteIfNeed(v temp)
}
Yaml_Quote(ByRef L,F,Q,B,ByRef E){
Return (F="\"&&!E&&(E:=1))||(E&&!(E:=0)&&(L:=L ("\" F)))
}
Yaml_SeqMap(o,k,v,isVal=0){
v:=Trim(v,A_Tab A_Space "`n"),m:=SubStr(v,1,1) SubStr(v,0)
If Yaml_IsSeqMap(v)
return m="[]"?Yaml_Seq(o,k,SubStr(v,2,StrLen(v)-2),isVal):m="{}"?Yaml_Map(o,k,SubStr(v,2,StrLen(v)-2),isVal):0
}
Yaml_Seq(obj,key,value,isVal=0){
static base:={Dump:"Yaml_Dump",Save:"Yaml_Save",Add:"Yaml_Add",Merge:"Yaml_Merge",__Delete:"__Delete",_Insert:"_Insert",_Remove:"_Remove",_GetCapacity:"_GetCapacity",_SetCapacity:"_SetCapacity",_GetAddress:"_GetAddress",_MaxIndex:"_MaxIndex",_MinIndex:"_MinIndex",_NewEnum:"_NewEnum",_HasKey:"_HasKey",_Clone:"_Clone",Insert:"Insert",Remove:"Remove",GetCapacity:"GetCapacity",SetCapacity:"SetCapacity",GetAddress:"GetAddress",MaxIndex:"MaxIndex",MinIndex:"MinIndex",NewEnum:"NewEnum",HasKey:"HasKey",Clone:"Clone",base:{__Call:"Yaml_Call"}}
ContinueNext:=0
If (obj=""){
If (SubStr(value,0)!="]")
Return 0
else
value:=SubStr(value,2,StrLen(value)-2)
} else {
If (key=""){
obj.Insert(Object("",cObj:=Object("base",base),"base",base))
} else if (isval && IsObject(obj[key,""])){
cObj:=obj[key,""]
} else obj[key]:=Object("",cObj:=Object("base",base),"base",base)
}
Count:=StrLen(value)
Loop,Parse,value
{
If ((Quote=""""&&Yaml_Quote(LF,A_LoopField,Quote,Bracket,Escape)) || (ContinueNext && !ContinueNext:=0))
Continue
If (Quote){
If (A_LoopField=Quote){
Quote=
If Bracket
LF.= A_LoopField
else LF:=SubStr(LF,2)
Continue
}
LF .= A_LoopField
continue
} else if (!Quote&&InStr("""'",A_LoopField)){
Quote:=A_LoopField
If !Bracket
VQ:=Quote
LF.=A_LoopField
Continue
} else if (!Quote&&Bracket){
If (Asc(A_LoopField)=Asc(Bracket)+2)
BCount--
else if (A_LoopField=Bracket)
BCount++
If (BCount=0)
Bracket=
LF .= A_LoopField
Continue
} else if (!Quote&&!Bracket&&InStr("[{",A_LoopField)){
Bracket:=A_LoopField
BCount:=1
LF.=A_LoopField
Continue
}
If (A_Index=Count)
LF .= A_LoopField
else if (!Quote&&!Bracket&&A_LoopField=","&&(!InStr("0123456789",SubStr(value,A_Index-1,1)) | !InStr("0123456789",SubStr(value,A_Index+1,1)))){
ContinueNext:=SubStr(value,A_Index+1,1)=A_Space||SubStr(value,A_Index+1,1)=A_Tab
LF:=LF
} else {
LF .= A_LoopField
continue
}
If (obj=""){
If !VQ
If (Asc(LF)=91 && !Yaml_Seq("","",LF))
||(Asc(LF)=123 && !Yaml_Map("","",LF))
Return 0
} else {
If (VQ || !Yaml_SeqMap(cObj,"",LF))
cObj.Insert(VQ?Yaml_UniChar(LF):Trim(LF))
}
LF:="",VQ:=""
}
If (LF){
If (obj=""){
If !VQ
If (Asc(LF)=91 && !Yaml_Seq("","",LF))||(Asc(LF)=123 && !Yaml_Map("","",LF))
Return 0
} else If (VQ || !Yaml_SeqMap(cObj,"",LF))
cObj.Insert(VQ?Yaml_UniChar(LF):Trim(LF))
}
Return (obj=""?(Quote Bracket=""):1)
}
Yaml_Map(obj,key,value,isVal=0){
static base:={Dump:"Yaml_Dump",Save:"Yaml_Save",Add:"Yaml_Add",Merge:"Yaml_Merge",__Delete:"__Delete",_Insert:"_Insert",_Remove:"_Remove",_GetCapacity:"_GetCapacity",_SetCapacity:"_SetCapacity",_GetAddress:"_GetAddress",_MaxIndex:"_MaxIndex",_MinIndex:"_MinIndex",_NewEnum:"_NewEnum",_HasKey:"_HasKey",_Clone:"_Clone",Insert:"Insert",Remove:"Remove",GetCapacity:"GetCapacity",SetCapacity:"SetCapacity",GetAddress:"GetAddress",MaxIndex:"MaxIndex",MinIndex:"MinIndex",NewEnum:"NewEnum",HasKey:"HasKey",Clone:"Clone",base:{__Call:"Yaml_Call"}}
ContinueNext:=0
If (obj=""){
If (SubStr(value,0)!="}")
Return 0
else
value:=SubStr(value,2,StrLen(value)-2)
} else {
If (key="")
obj.Insert(cObj:=Object("base",base))
else obj[key]:=(cObj:=Object("base",base))
}
Count:=StrLen(value)
Loop,Parse,value
{
If ((Quote=""""&&Yaml_Quote(LF,A_LoopField,Quote,Bracket,Escape)) || (ContinueNext && !ContinueNext:=0))
Continue
If (Quote){
If (A_LoopField=Quote){
Quote=
LF.=A_LoopField
} else LF .= A_LoopField
continue
} else if (!Quote&&(k=""||v="")&&InStr("""'",A_LoopField)){
Quote:=A_LoopField
If (k && !Bracket)
VQ:=Quote
else if !Bracket
KQ:=Quote
LF.=Quote
Continue
} else If (k!=""&&LF=""&&InStr("`n`r `t",A_LoopField)){
Continue
}
If (!Quote&&Bracket){
If (Asc(A_LoopField)=Asc(Bracket)+2)
BCount--
else if (A_LoopField=Bracket)
BCount++
If (BCount=0)
Bracket=
LF .= A_LoopField
Continue
} else if (!Quote&&!Bracket&&InStr("[{",A_LoopField)){
Bracket:=A_LoopField
BCount=1
LF.=A_LoopField
Continue
}
If (A_Index=Count&&k!=""){
v:=LF A_LoopField
v:=Trim(v)
If (InStr("""'",SubStr(v,0))&&SubStr(v,1,1)=SubStr(v,0))
v:=SubStr(v,2,StrLen(v)-2)
} else If (!Quote&&!Bracket&&k!=""&&A_LoopField=","&&SubStr(value,A_Index+1,1)=A_Space){
ContinueNext:=1
LF:=Trim(LF)
If VQ
LF:=SubStr(LF,2,StrLen(LF)-2)
v:=LF,LF:=""
} else if (!Quote&&!Bracket&&k=""&&A_LoopField=":"){
LF:=Trim(LF)
If (InStr("""'",SubStr(LF,0))&&SubStr(LF,1,1)=SubStr(LF,0))
LF:=SubStr(LF,2,StrLen(LF)-2)
k:=LF,LF:=""
continue
} else {
LF .= A_LoopField
continue
}
If (obj=""){
If VQ=
If (Asc(v)=91 && !Yaml_Seq("","",v))
||(Asc(v)=123 && !Yaml_Map("","",v))
Return 0
} else {
If (VQ || !Yaml_SeqMap(cObj,k,v))
cObj[KQ?Yaml_UniChar(k):k]:=(VQ?Yaml_UniChar(v):Trim(v))
}
k:="",v:="",VQ:="",KQ:=""
}
If (k){
If (obj=""){
If (Asc(LF)=91 && !Yaml_Seq("","",LF))||(Asc(LF)=123 && !Yaml_Map("","",LF))
Return 0
} else {
LF:=Trim(LF)
If (VQ)
LF:=SubStr(LF,2,StrLen(LF)-2),cObj[k]:=Yaml_UniChar(LF)
else If (!Yaml_SeqMap(cObj,k,LF))
cObj[k]:=Trim(LF)
}
}
Return (obj=""?(Quote Bracket=""):1)
}
Yaml_Incomplete(value){
return (Asc(Trim(value,"`n" A_Tab A_Space))=91 && !Yaml_Seq("","",Trim(value,"`n" A_Tab A_Space)))
|| (Asc(Trim(value,"`n" A_Tab A_Space))=123 && !Yaml_Map("","",Trim(value,"`n" A_Tab A_Space)))
}
Yaml_IsSeqMap(value){
return (Asc(Trim(value,"`n" A_Tab A_Space))=91 && Yaml_Seq("","",Trim(value,"`n" A_Tab A_Space)))
|| (Asc(Trim(value,"`n" A_Tab A_Space))=123 && Yaml_Map("","",Trim(value,"`n" A_Tab A_Space)))
}
ObjTree(ByRef obj,Title="ObjTree",Options="+ReadOnly +Resize,GuiShow=w640 h480",ishwnd=-1){
; Version 1.0.1.0
static
; TREEOBJ will hold all running windows and some information about them
static TREEOBJ:={},parents:={},ReadOnly:={},ReadOnlyLevel:={},objects:={},newObj:={},hwnd:={},EditItem:={},EditKey:={},EditObject:={},TT:={},Changed:={}
,LV_SortArrow:="LV_SortArrow",ToolTipText,EditValue,G,WM_NOTIFY:=0x4e
; OnMessage WM_NOTIFY structure
static HDR:=new _Struct("HWND hwndFrom,UINT_PTR idFrom,UINT code,LPTSTR pszText,int cchTextMax,HANDLE hItem,LPARAM lParam") ;NMTVGETINFOTIP
static TVN_FIRST := 0xfffffe70,TVN_GETINFOTIP := TVN_FIRST - 14 - (A_IsUnicode?0:1),MenuExist:=0
local Font:="",GuiShow:="",GuiOptions:="",TREEHWND:="",EDITHWND:="",_EditKey:="",DefaultGui:="",FocusedControl:="",Height:="",Width:="",k:="",v:="",Item:="",NewKey:=""
,option:="",option1:="",option2:="",pos:="",thisHwnd:="",toRemove:="",object:="",TV_Child:="",TV_Item:="",TV_Text:="",TVC:="",TVP:="",LV_CurrRow:="",opt:=""
If (ishwnd!=-1&&!IsObject(ishwnd)){
/*
ObjTree is also used to Monitor messages for TreeView: ObjeTree(obj=wParam,Title=lParam,Options=msg,ishwnd=hwnd)
when ishwnd is a handle, this routine is taken
*/
; Using _Struct class we can assign new pointer to our structure
; This way the structure is available a lot faster and less CPU is used
HDR[]:=Title
; Check if this message is relevant for our windows
If (HDR.code!=TVN_GETINFOTIP || !TREEOBJ["_" HDR.hwndFrom])
Return
; Set Default Gui
Gui,% (G:=TREEOBJ["_" HDR.hwndFrom]) ":Default"
; Set Default TreeView
Gui,TreeView, ObjTreeTreeView%G%
; HDR.Item contains the relevant TV_ID
TV_GetText(TV_Text,TV_Item:=HDR.hItem)
; Check if this GUI uses a ToolTip object that contains the information in same structure as the TreeView
If ToolTipText:=TT[G] { ; Gui has own ToolTip object
; following will resolve the item in ToolTip object
object:=[TV_Text]
While TV_Item:=TV_GetParent(TV_Item){
TV_GetText(k,TV_Item)
object.Insert(k)
}
; Resolve our item/value in ToolTip object
While object.MaxIndex(){
ToolTipText:=ToolTipText[object.Remove()]
}
; Item is not an object and is not empty, display value in ToolTip
If (ToolTipText!="")
Return HDR.pszText[""]:=&ToolTipText
}
; Gui has no ToolTip object or item could not be resolved
; Get the value of item and display in ToolTip
object:=parents[G,HDR.hItem]
; Check if Item is an object and if so, display first 20 keys (first 50 chars) and values (first 100 chars)
If (Parents[G,object]!=HDR.hItem){
for key,v in object
If ((IsObject(key)?(Chr(177) " " (&key)):key)=TV_Text){
If !IsObject(v)
Return ToolTipText:=v,HDR.pszText[""]:=&ToolTipText
else If IsFunc(object)
ToolTipText:="[Func]`t`t" object.Name "`nBuildIn:`t`t" object.IsBuiltIn "`nVariadic:`t" object.IsVariadic "`nMinParams:`t" object.MinParams "`nMaxParams:`t" object.MaxParams
else
for key,v in v
{
ToolTipText.=(ToolTipText?"`n":"") SubStr(key,1,50) (StrLen(key)>50?"...":"") " = " (IsObject(v)?"[Obj] ":SubStr(v,1,100) (StrLen(v)>100?"...":""))
If (A_Index>20){
ToolTipText.="`n...s"
break
}
}
Return HDR.pszText[""]:=&ToolTipText
}
for key,v in Parents[G,HDR.hItem]
{
ToolTipText.=(ToolTipText?"`n":"") SubStr(key,1,50) (StrLen(key)>50?"...":"") " = " (IsObject(v)?"[Obj] ":SubStr(v,1,100) (StrLen(v)>100?"...":""))
If (A_Index>20){
ToolTipText.="`n...s"
break
}
}
Return HDR.pszText[""]:=&ToolTipText
} else { ; Item is an object, display
ToolTipText:=""
If IsFunc(object)
ToolTipText:="[Func]`t`t" object.Name "`nBuildIn:`t`t" object.IsBuiltIn "`nVariadic:`t" object.IsVariadic "`nMinParams:`t" object.MinParams "`nMaxParams:`t" object.MaxParams
else
for key,v in object
{
ToolTipText.=(ToolTipText?"`n":"") SubStr(key,1,50) (StrLen(key)>50?"...":"") " = " (IsObject(v)?"[Obj] ":SubStr(v,1,100) (StrLen(v)>100?"...":""))
If (A_Index>20){
ToolTipText.="`n...s"
break
}
}
HDR.pszText[""]:= &ToolTipText
}
Return
}
; Function was not called by Message to the window
; Create a new ObjTree window
; Get the hwnd of default GUI to restore it later
Gui,+HwndDefaultGui
;find free gui starting at 30
Loop % (G := 30) {
Gui %G%:+LastFoundExist
If !WinExist() ;gui is free
break
G++
}
; Custom ToolTip object
; It needs to have same keys/object structure as main object
; You can leave out keys, then their value will be show
If IsObject(ishwnd)
TT[G]:=ishwnd
else TT[G]:=""
; Apply properties and Gui options
If (Options="")
Options:="+AlwaysOnTop +Resize,GuiShow=w640 h480"
If RegExMatch(Options,"i)^\s*([-\+]?ReadOnly)(\d+)?\s*$",option)
Options:="+AlwaysOnTop +Resize,GuiShow=w640 h480",ReadOnly[G]:=option1,ReadOnlyLevel[G]:=option2
else ReadOnly[G]:="+ReadOnly"
Loop, Parse, Options, `,, %A_Space%
{
opt := Trim(SubStr(A_LoopField,1,InStr(A_LoopField,"=")-1))
If RegExMatch(A_LoopField,"i)([-\+]?ReadOnly)(\d+)?",option)
ReadOnly[G]:=option1,ReadOnlyLevel[G]:=option2
If (InStr("Font,GuiShow,NoWait",opt))
%opt% := SubStr(A_LoopField,InStr(A_LoopField,"=") + 1,StrLen(A_LoopField))
else GuiOptions:=RegExReplace(A_LoopField,"i)[-\+]?ReadOnly\s?")
}
; Set new default Gui
Gui,%G%:Default
; Set font
if Font
Gui, Font, % SubStr(Font,1,Pos := InStr(Font,":") - 1), % SubStr(Font,Pos + 2,StrLen(Font))
; Get Gui size
RegExMatch(GuiShow,"\b[w]([0-9]+\b).*\b[h]([0-9]+\b)",size)
; Get hwnd of new window
Gui,+HwndthisHwnd
; Hwnd will be required later so save it
hWnd[G]:=thisHwnd
; Apply Gui options and create Gui
Gui,%GuiOptions% +LastFound +LabelObjTree__
Gui,Add,Button, x0 y0 NoTab Hidden Default gObjTree_ButtonOK,Show/Expand Object
Gui,Add,TreeView,% "xs w" (size1*0.3) " h" size2 " ys AltSubmit gObjTree_TreeView +0x800 hwndTREEHWND " ReadOnly[G] " vObjTreeTreeView" G
Gui,Add,ListView,% "x+1 w" (size1*0.7) " h" (size2*0.5) " ys AltSubmit Checked " ReadOnly[G] " gObjTree_ListView hwndLISTHWND" G " vObjTreeListView" G,[IsObj] Key/Address|Value/Address
Gui,Add,Edit,% "y+1 w" (size1*0.7) " h" (size2*0.5) " -wrap +HScroll gObjTree_Edit HWNDEDITHWND " ReadOnly[G]
GuiControl,Disable,Edit1
TREEOBJ["_" (TREEHWND+0)] := G
Attach(TREEHWND,"w1/2 h")
Attach(LISTHWND%G%,"w1/2 h1/2 x1/2 y0")
Attach(EDITHWND,"w1/2 h1/2 x1/2 y1/2")
; parents will hold TV_Item <> Object relation
parents[G]:={}
Gui,Show,%GuiShow%,%Title%
return
; Convert object to TreeView
; Also create a clone for our object
; Changes can be optionally saved when ObjTree is closed when -ReadOnly is used
If (ReadOnly[G]="-ReadOnly")
newObj[G]:=ObjTree_Clone(Objects[G]:=obj),parents[G,newObj[G]]:=0,ObjTree_Add(newObj[G],0,parents,G)
else parents[G,obj]:=0,ObjTree_Add(Objects[G]:=obj,0,parents,G)
Gui,Show,%GuiShow%,%Title%
return
; Create Menus to be used for all ObjTree windows (ReadOnly windows have separate Menu)
if !MenuExist {
Menu,ObjTree,Add,&Expand,ObjTree_ExpandSelection
Menu,ObjTree,Add,&Collapse,ObjTree_CollapseSelection
Menu,ObjTree,Add
Menu,ObjTree,Add,E&xpand All,ObjTree_ExpandAll
Menu,ObjTree,Add,C&ollapse All,ObjTree_CollapseAll
Menu,ObjTree,Add
Menu,ObjTree,Add,&Insert,ObjTree_Insert
Menu,ObjTree,Add,I&nsertChild,ObjTree_InsertChild
Menu,ObjTree,Add
Menu,ObjTree,Add,&Delete,ObjTree_Delete
Menu,ObjTreeReadOnly,Add,&Expand,ObjTree_ExpandSelection
Menu,ObjTreeReadOnly,Add,&Collapse,ObjTree_CollapseSelection
Menu,ObjTreeReadOnly,Add
Menu,ObjTreeReadOnly,Add,E&xpand All,ObjTree_ExpandAll
Menu,ObjTreeReadOnly,Add,C&ollapse All,ObjTree_CollapseAll
MenuExist:=1
}
; Register to Launch this function OnMessage
OnMessage(WM_NOTIFY,"ObjTree")
; Show our Gui
Gui,Show,%GuiShow%,%Title%
; Restore Default Gui
If DefaultGui
Gui, %DefaultGui%:Default
; Return hwnd of new ObjTree window
Return thisHwnd
; Backup current Gui number and display Menu
ObjTree__ContextMenu:
G:=A_Gui
If (ReadOnly[G]!="-ReadOnly")
Menu,ObjTreeReadOnly,Show
else Menu,ObjTree,Show
Return
; Insert new Item, launched by Menu
ObjTree_Insert:
If A_Gui
G:=A_Gui
If (ReadOnly[G]!="-ReadOnly")
return
Changed[G]:=1
Gui,%G%:Default
Gui,TreeView, ObjTreeTreeView%G%
Gui,ListView, ObjTreeListView%G%
TV_Item:=TV_GetSelection()
Loop % ReadOnlyLevel[G]
If !(TV_Item:=TV_GetParent(TV_Item))
Return
EditItem[G]:=TV_GetParent(TV_Child:=TV_GetSelection())
,EditObject[G]:=!EditItem[G]?newObj[G]:parents[G,EditItem[G]]
,EditObject[G].Insert("")
,Parents[G,EditItem[G]:=TV_Add(EditObject[G].MaxIndex(),EditItem[G],"Sort")]:=EditObject[G]
,TV_Modify(EditItem[G],"Select")
,TV_GetText(TV_Text,TV_Item)
return
; Insert new Child item, launched by Menu
ObjTree_InsertChild:
If A_Gui
G:=A_Gui
If (ReadOnly[G]!="-ReadOnly")
return
Changed[G]:=1
Gui,%G%:Default
Gui,TreeView, ObjTreeTreeView%G%
Gui,ListView, ObjTreeListView%G%
TV_Item:=TV_GetSelection()
Loop % ReadOnlyLevel[G]
If !(TV_Item:=TV_GetParent(TV_Item))
Return
EditItem[G]:=TV_GetParent(TV_Child:=TV_GetSelection())
EditObject[G]:=!EditItem[G]?newObj[G]:parents[G,EditItem[G]]
TV_GetText(_EditKey,TV_Child)
If IsObject(EditObject[G,EditKey[G]:=_EditKey]){
_EditKey:=EditObject[G,EditKey[G]].MaxIndex()+1
EditObject[G,EditKey[G]].Insert(NewKey:=_EditKey?_EditKey:1,"") ;,ObjTree_Add(EditObject[G,EditKey[G]],TV_Child,parents[G])
,Parents[G,EditItem[G]:=TV_Add(EditObject[G,EditKey[G]].MaxIndex(),TV_Child,"Sort")]:=EditObject[G,EditKey[G]]
,ObjTree_LoadList(EditObject[G,EditKey[G]],"",G)
} else
EditObject[G,EditKey[G]]:=NewKey:={1:""},parents[G,TV_Child]:=NewKey,parents[G,NewKey]:=TV_Child
,ObjTree_Add(NewKey,TV_Child,parents,G)
TV_Modify(TV_Child,"Expand")
DllCall("InvalidateRect", "ptr", hwnd[G], "ptr", 0, "int", true)
return
; Delete Item, launched by Menu
ObjTree_Delete:
If A_Gui
G:=A_Gui
If (ReadOnly[G]!="-ReadOnly")
return
Changed[G]:=1
Gui,%G%:Default
Gui,TreeView, ObjTreeTreeView%G%
Gui,ListView, ObjTreeListView%G%
TV_Item:=TV_GetSelection()
Loop % ReadOnlyLevel[G]
If !(TV_Item:=TV_GetParent(TV_Item))
Return
EditObject[G]:=!TV_GetParent(TV_GetSelection())?newObj[G]:parents[G,TV_GetParent(TV_GetSelection())]
TV_GetText(_EditKey,TV_Item:=TV_GetSelection())
ObjRemove(EditObject[G],EditKey[G]:=_EditKey)
for key in EditObject[G]
{
EditKey[G]:=key
break
}
ObjTree_TVReload(EditObject[G],TV_Item,EditKey[G],parents,G)
Return
return
; Close ObjTree Window
ObjTree__Close:
If A_Gui
G:=A_Gui
Gui,%G%:+OwnDialogs
If (ReadOnly[G]="-ReadOnly" && Changed[G]){
MsgBox,3,Save Changes,Would you like to save changes?
IfMsgBox Cancel
Return
IfMsgBox Yes
{
toRemove:={}
for key in Objects[G]
toRemove[k]:=key
for key in toRemove
Objects[G].Remove(key)
for key,v in newObj[G]
Objects[G,key]:=v
}
}
Gui,%G%:Destroy
newObj[G]:="",Objects[G]:="",parents[G]:="",hwnd[G]:="",TREEOBJ[G]:="",Readonly[G]:="",ReadOnlyLevel[G]:=""
Return
; Edit control event, update value in ListView and clone of our object
ObjTree_Edit:
If A_Gui
G:=A_Gui
Gui,%G%:Default
Gui,TreeView, ObjTreeTreeView%G%
Gui,ListView, ObjTreeListView%G%
If (ReadOnly[G]!="-ReadOnly"||!(EditItem[G]:=LV_GetNext(0)))
Return
TV_Item:=TV_GetSelection()
Loop % ReadOnlyLevel[G]-1
If !(TV_Item:=TV_GetParent(TV_Item)){
LV_GetText(_EditKey,EditItem[G],2)
ControlSetText,Edit1,% EditKey[G]:=_EditKey,% "ahk_id " hwnd[G]
Return
}
EditObject[G]:=!TV_GetParent(TV_GetSelection())?newObj[G]:parents[G,TV_GetParent(TV_GetSelection())]
LV_GetText(_EditKey,EditItem[G])
If IsObject(EditObject[G,EditKey[G]:=_EditKey]){
GuiControl,,Edit1
GuiControl,Disable,Edit1
Return
}
ControlGetText,EditValue,Edit1,% "ahk_id " hwnd[G]
LV_Modify(EditItem[G],"",EditKey[G],EditValue)
EditObject[G,EditKey[G]]:=EditValue
Changed[G]:=1
Return
; TreeView events handling
ObjTree_TreeView:
If A_Gui
G:=A_Gui
Gui,%G%:Default
Gui,TreeView, ObjTreeTreeView%G%
If (ReadOnly[G]="-ReadOnly"){
If (A_GuiEvent=="E"||(A_GuiEvent="k"&&A_EventInfo=113)){
If (A_GuiEvent="k")
EditItem[G]:=TV_GetSelection()
else EditItem[G]:=A_EventInfo
EditObject[G]:=!TV_GetParent(EditItem[G])?newObj[G]:parents[G,TV_GetParent(EditItem[G])]
TV_GetText(_EditKey,EditItem[G])
EditKey[G]:=_EditKey
Return
} else if (EditItem[G]&&A_GuiEvent=="e"){
TV_Item:=TV_GetSelection()
Loop % ReadOnlyLevel[G]
If !(TV_Item:=TV_GetParent(TV_Item))
Return, TV_Modify(EditItem[G],"Sort",EditKey[G])
TV_GetText(NewKey,EditItem[G])
If (NewKey=EditKey[G])
Return
else if EditObject[G].HasKey(NewKey){
Gui,%G%: +OwnDialogs
MsgBox,4,Existing Item,The new item already exist.`nDo you want to replace it with this item?
IfMsgBox No
Return TV_Modify(EditItem[G],"",EditKey[G])
Changed[G]:=1
EditObject[G,NewKey]:=EditObject[G,EditKey[G]],ObjRemove(EditObject[G],EditKey[G])
return ObjTree_TVReload(EditObject[G],EditItem[G],NewKey,parents,G)
} else {
Changed[G]:=1
EditObject[G,NewKey]:=EditObject[G,EditKey[G]],ObjRemove(EditObject[G],EditKey[G])
return ObjTree_TVReload(EditObject[G],EditItem[G],NewKey,parents,G)
}
} else If (A_GuiEvent="k"){ ;Key Press
If A_EventInfo not in 45,46
Return
TV_Item:=TV_GetSelection()
Loop % ReadOnlyLevel[G]
If !(TV_Item:=TV_GetParent(TV_Item))
Return
If (A_EventInfo=45){ ;Insert + Shift && Insert
Changed[G]:=1
EditItem[G]:=TV_GetParent(TV_Child:=TV_GetSelection())
EditObject[G]:=!EditItem[G]?newObj[G]:parents[G,EditItem[G]]
If (GetKeyState("Shift","P")&&!ReadOnlyLevel[G])
EditObject[G].Insert(EditKey[G]:={1:""})
,parents[G,EditItem[G]:=TV_Add(EditObject[G].MaxIndex(),EditItem[G],"Sort")]:=EditKey[G],parents[G,EditKey[G]]:=EditItem[G]
,ObjTree_Add(EditKey[G],EditItem[G],parents,G)
else if (GetKeyState("CTRL","P")&&!ReadOnlyLevel[G]){
TV_GetText(_EditKey,TV_Child)
If IsObject(EditObject[G,EditKey[G]:=_EditKey]){
_EditKey:=EditObject[G,EditKey[G]].MaxIndex()+1
EditObject[G,EditKey[G]].Insert(_EditKey?_EditKey:1,"")
,Parents[G,EditItem[G]:=TV_Add(EditObject[G,EditKey[G]].MaxIndex(),TV_Child,"Sort")]:=EditObject[G,EditKey[G]]
,ObjTree_LoadList(EditObject[G,EditKey[G]],"",G)
} else
EditObject[G,EditKey[G]]:=NewKey:={1:""},parents[G,TV_Child]:=NewKey,parents[G,NewKey]:=TV_Child
,ObjTree_Add(NewKey,TV_Child,parents,G),TV_Modify(TV_Child,"Expand")
} else
EditObject[G].Insert("")
,parents[G,EditItem[G]:=TV_Add(EditObject[G].MaxIndex(),EditItem[G],"Sort")]:=EditObject[G]
} else if (A_EventInfo=46) { ;Delete
Changed[G]:=1
EditObject[G]:=!TV_GetParent(TV_GetSelection())?newObj[G]:parents[G,TV_GetParent(TV_GetSelection())]
TV_GetText(_EditKey,TV_Item:=TV_GetSelection())
ObjRemove(EditObject[G],EditKey[G]:=_EditKey)
for key in EditObject[G]
{
EditKey[G]:=key
break
}
return ObjTree_TVReload(EditObject[G],TV_Item,EditKey[G],parents,G)
}
EditKey[G]:="",EditObject[G]:="",EditItem[G]:=""
GuiControl, +Redraw, ObjTreeTreeView
DllCall("InvalidateRect", "ptr", hwnd[G], "ptr", 0, "int", true)
Return
}
} else if A_GuiEvent in k,E,e
Return
if (A_EventInfo=0)
Return
If (A_GuiEvent="-"){
TV_Modify(A_EventInfo,"-Expand")
Return
}
TV_GetText(TV_Text,A_EventInfo)
TV_Modify(A_EventInfo,"Select")
If parents[G].HasKey(A_EventInfo)
ObjTree_LoadList(parents[G,A_EventInfo],TV_Text,G)
else
ObjTree_LoadList(parents[G,TV_GetParent(A_EventInfo)],TV_Text,G)
Return
; ListView events handling
ObjTree_ListView:
If A_Gui
G:=A_Gui
Gui,%G%:Default
Gui,TreeView, ObjTreeTreeView%G%
Gui,ListView, ObjTreeListView%G%
If (ReadOnly[G]="-ReadOnly"){
If (A_GuiEvent=="E"){
LV_GetText(_EditKey,A_EventInfo),EditKey[G]:=_EditKey,EditItem[G]:=TV_GetSelection()
EditObject[G]:=!TV_GetParent(EditItem[G])?newObj[G]:parents[G,TV_GetParent(TV_GetSelection())]
} else If (A_GuiEvent=="e"){
TV_Item:=TV_GetSelection()
Loop % ReadOnlyLevel[G]
If !(TV_Item:=TV_GetParent(TV_Item)){
LV_Modify(A_EventInfo,"",EditKey[G])
Return
}
LV_GetText(NewKey,A_EventInfo)
If (NewKey=EditKey[G])
Return
else if EditObject[G].HasKey(NewKey) {
Gui,%G%: +OwnDialogs
MsgBox,4,Existing Item,The new item already exist.`nDo you want to replace it with this item?
IfMsgBox No
{
LV_Modify(A_EventInfo,"",EditKey[G])
Return
}
Changed[G]:=1
EditObject[G,NewKey]:=EditObject[G,EditKey[G]],ObjRemove(EditObject[G],EditKey[G])
return ObjTree_TVReload(EditObject[G],EditItem[G],NewKey,parents,G)
} else {
Changed[G]:=1
EditObject[G,NewKey]:=EditObject[G,EditKey[G]],ObjRemove(EditObject[G],EditKey[G])
ObjTree_TVReload(EditObject[G],EditItem[G],NewKey,parents,G)
return
}
}
}
If (A_GuiEvent = "ColClick"){
Return ,IsFunc(LV_SortArrow)?LV_SortArrow.(LISTHWND%G%, A_EventInfo):""
} else if (A_GuiEvent="k" && A_EventInfo=8){
If TV_GetParent(TV_GetSelection())
TV_Modify(TV_GetParent(TV_GetSelection()))
Gui,+LastFound
ControlFocus,ObjTreeListView%G%
return
} else if (A_GuiEvent="Normal"){
GuiControl,,Edit1
GuiControl,Disable,Edit1
TV_Item:=TV_GetSelection()
If !(TV_Child:=TV_GetChild(TV_Item))
TV_Item:=TV_GetParent(TV_Item),TV_Child:=TV_GetChild(TV_Item)
If (!TV_GetNext(TV_Child) && TV_GetChild(TV_Child) && TV_GetText(TVP,TV_Child) && TV_GetText(TVC,TV_GetParent(TV_Child)) && TVC=TVP)
If TV_GetParent(TV_Child)
TV_Child:=TV_GetParent(0)
else
TV_Child:=TV_GetNext()
If !TV_Child
TV_Child:=TV_GetSelection()
LV_GetText(LV_Item,A_EventInfo,1)
While (TV_GetText(TV_Item,TV_Child) && TV_Item!=LV_Item)
TV_Child:=TV_GetNext(TV_Child)
If !TV_Child
Return
; If (parents[G,parents[G,TV_Child]]!=TV_Child)
; If !(TV_Child:=parents[G,parents[G,TV_Child]])
; TV_Child:=TV_GetNext()
If (TV_GetSelection()!=TV_Child)
TV_Modify(TV_Child,"Select Expand")
for key,v in parents[G,TV_Child]
If (key=LV_Item || (Chr(177) " " (&key))=LV_Item){
GuiControl,,Edit1,% parents[G,TV_Child,key]
GuiControl,Enable,Edit1
Break
}
Return
} else if (A_GuiEvent!="DoubleClick" && !(A_GuiEvent="I" && ErrorLevel="C"))
Return
; Hidden Default Button
; Used when Enter is pressed but also used by TreeView and ListView
ObjTree_ButtonOK:
If A_Gui
G:=A_Gui
Gui,%G%:Default
Gui,TreeView, ObjTreeTreeView%G%
Gui,ListView, ObjTreeListView%G%
If (A_ThisLabel="ObjTree_ButtonOK"){
GuiControlGet, FocusedControl, FocusV
if (FocusedControl = "ObjTreeListView" G){
Item:=LV_GetNext(0)
} else if (FocusedControl = "ObjTreeTreeView" G){
TV_Modify(TV_GetSelection(),"Expand")
Return
}
If !Item
Return
} else Item:=A_EventInfo
TV_Item:=TV_GetSelection()
If !(TV_Child:=TV_GetChild(TV_Item))
TV_Item:=TV_GetParent(TV_Item),TV_Child:=TV_GetChild(TV_Item)
If (!TV_GetNext(TV_Child) && TV_GetChild(TV_Child) && TV_GetText(TVP,TV_Child) && TV_GetText(TVC,TV_GetParent(TV_Child)) && TVC=TVP)
If TV_GetParent(TV_Child)
TV_Child:=TV_GetParent(0)
else
TV_Child:=TV_GetNext()
If !TV_Child
TV_Child:=TV_GetSelection()
LV_GetText(LV_Item,Item,1)
While (TV_GetText(TV_Item,TV_Child) && TV_Item!=LV_Item)
TV_Child:=TV_GetNext(TV_Child)
If (A_GuiEvent="I" && ErrorLevel="C")
LV_Modify(Item,(parents[G,parents[G,TV_Child]]=TV_Child?"":"-")"Check")
else if (TV_Child)
TV_Modify(TV_Child,"Select Expand")
Return
ObjTree_ExpandAll:
Gui,%G%:Default
Gui,TreeView, ObjTreeTreeView%G%
Gui,ListView, ObjTreeListView%G%
ObjTree_Expand(TV_GetNext())
Return
ObjTree_ExpandSelection:
Gui,%G%:Default
Gui,TreeView, ObjTreeTreeView%G%
Gui,ListView, ObjTreeListView%G%
ObjTree_Expand(TV_GetSelection(),1)
Return
ObjTree_CollapseAll:
Gui,%G%:Default
Gui,TreeView, ObjTreeTreeView%G%
Gui,ListView, ObjTreeListView%G%
ObjTree_Expand(TV_GetNext(),0,1)
TV_Modify(TV_GetNext())
Return
ObjTree_CollapseSelection:
Gui,%G%:Default
Gui,TreeView, ObjTreeTreeView%G%
Gui,ListView, ObjTreeListView%G%
ObjTree_Expand(TV_GetSelection(),1,1)
Return
}
ObjTree_Expand(TV_Item,OnlyOneItem=0,Collapse=0){
Loop {
If !TV_GetChild(TV_Item)
TV_Modify(TV_GetParent(TV_Item),(Collapse?"-":"") "Expand")
else TV_Modify(TV_Item,(Collapse?"-":"") "Expand")
If (TV_Child:=TV_GetChild(TV_Item))
ObjTree_Expand(TV_Child,0,Collapse)
} Until (OnlyOneItem || (!TV_Item:=TV_GetNext(TV_Item)))
}
ObjTree_Add(obj,parent,ByRef p,G){
k:="",v:=""
for k,v in obj
{
If (IsObject(v) && !p[G].Haskey(v))
p[G,v]:=TV_Add(IsObject(k)?Chr(177) " " (&k):k,parent,"Sort"),p[G,p[G,v]]:=v
,ObjTree_Add(v,p[G,v],p,G)
else
p[G,lastParent:=TV_Add(IsObject(k)?Chr(177) " " (&k):k,parent,"Sort")]:=IsObject(v)?v:obj
If (IsObject(k) && !p[G].HasKey(v))
p[G,k]:=TV_Add(Chr(177) " " (&k),IsObject(v)?p[G,v]:lastParent,"Sort"),p[G,p[G,k]]:=k
,ObjTree_Add(k,p[G,k],p,G)
}
}
ObjTree_Clone(obj,e=0){
k:="",v:=""
If !e
e:={(obj):clone:=obj._Clone()}
else If !e.HasKey(obj)
e[obj]:=clone:=obj._Clone()
for k,v in obj
{
If IsObject(v){
If (IsObject(n:=k) && !e.HasKey(n))
n:=ObjTree_Clone(k,e)
else if e.HasKey(n)
n:=e[n]
If !e.HasKey(v){
e[v]:=clone[n]:=IsFunc(v)?v:ObjTree_Clone(v,e)
} else clone[n]:=e[v]
} else If IsObject(k) {
If !e.HasKey(k){
clone[n:=ObjTree_Clone(k,e)]:=e[k]:=clone[n],ObjRemove(clone,k)
} else clone[e[k]]:=v,ObjRemove(clone,k)
}
}
Return clone
}
ObjTree_TVReload(ByRef obj,TV_Item,Key,ByRef parents,G){ ; Version 1.0.1.0 http://www.autohotkey.com/forum/viewtopic.php?t=69756
Gui,%G%:Default
Gui,TreeView, ObjTreeTreeView%G%
If !(TV_Child:=TV_GetParent(TV_Item))
TV_Delete(),parents[G]:={}
else {
While % TV_GetChild(TV_Child)
TV_Delete(TV_GetChild(TV_Child))
}
ObjTree_Add(obj,TV_Child,parents,G)
ObjTree_LoadList(obj,Key,G)
GuiControl, +Redraw, ObjTreeTreeView
TV_Child:=TV_GetChild(TV_Child)
While (TV_GetText(TV_Item,TV_Child) && TV_Item!=Key){
TV_Child:=TV_GetNext(TV_Child)
}
TV_Modify(TV_Child,"Select") ;select item
DllCall("InvalidateRect", "ptr", hwnd[G], "ptr", 0, "int", true)
}
ObjTree_LoadList(obj,text,G){
LV_CurrRow:=""
Gui,%G%:Default
Gui,ListView, ObjTreeListView%G%
select:=!TV_GetChild(TV_GetSelection())
LV_Delete()
GuiControl,,Edit1,
GuiControl,Disable,Edit1
for k,v in obj
{
LV_Add(((IsObject(v)||IsObject(k))?"Check":"") (select&&text=(IsObject(k)?(Chr(177) " " (&k)):k)?(" Select",LV_CurrRow:=A_Index):"")
,IsObject(k)?(Chr(177) " " (&k)):k,IsFunc(v)?"[" (v.IsBuiltIn?"BuildIn ":"") (v.IsVariadic?"Variadic ":"") "Func] " v.Name:IsObject(v)?(Chr(177) " " (&v)):v)
If (LV_CurrRow=A_Index){
LV_Modify(LV_CurrRow,"Vis Select") ;make sure selcted row it is visible
GuiControl,Enable,Edit1
GuiControl,,Edit1,%v%
}
}
Loop 2
LV_ModifyCol(A_Index,"AutoHdr") ;autofit contents
}
/*
Function: Attach
Determines how a control is resized with its parent.
hCtrl:
- hWnd of the control if aDef is not empty.
- hWnd of the parent to be reset if aDef is empty. If you omit this parameter function will use
the first hWnd passed to it.
With multiple parents you need to specify which one you want to reset.
- Handler name, if parameter is string and aDef is empty. Handler will be called after the function has finished
moving controls for the parent. Handler receives hWnd of the parent as its only argument.
aDef:
Attach definition string. Space separated list of attach options. If omitted, function working depends on hCtrl parameter.
You can use following elements in the definition string:
- "x,y,w,h" letters along with coefficients, decimal numbers which can also be specified in m/n form (see example below).
- "r". Use "r1" (or "r") option to redraw control immediately after repositioning, set "r2" to delay redrawing 100ms for the control
(prevents redrawing spam).
- "p" (for "proportional") is the special coefficient. It will make control's dimension always stay in the same proportion to its parent
(so, pin the control to the parent). Although you can mix pinned and non-pinned controls and dimensions that is rarely what you want.
You will generally want to pin every control in the parent.
- "+" or "-" enable or disable function for the control. If control is hidden, you may want to disable the function for
performance reasons, especially if control is container attaching its children. Its perfectly OK to leave invisible controls
attached, but if you have lots of them you can use this feature to get faster and more responsive updates.
When you want to show disabled hidden control, make sure you first attach it back so it can take its correct position
and size while in hidden state, then show it. "+" must be used alone while "-" can be used either alone or in Attach definition string
to set up control as initially disabled.
Remarks:
Function monitors WM_SIZE message to detect parent changes. That means that it can be used with other eventual container controls
and not only top level windows.
You should reset the function when you programmatically change the position of the controls in the parent control.
Depending on how you created your GUI, you might need to put "autosize" when showing it, otherwise resetting the Gui before its
placement is changed will not work as intented. Autosize will make sure that WM_SIZE handler fires. Sometimes, however, WM_SIZE
message isn't sent to the window. One example is for instance when some control requires Gui size to be set in advance in which case
you would first have "Gui, Show, w100 h100 Hide" line prior to adding controls, and only Gui, Show after controls are added. This
case will not trigger WM_SIZE message unless AutoSize is added.
Examples:
(start code)
Attach(h, "w.5 h1/3 r2") ;Attach control's w, h and redraw it with delay.
Attach(h, "-") ;Disable function for control h but keep its definition. To enable it latter use "+".
Attach(h, "- w.5") ;Make attach definition for control but do not attach it until you call Attach(h, "+").
Attach() ;Reset first parent. Use when you have only 1 parent.
Attach(hGui2) ;Reset Gui2.
Attach("Win_Redraw") ;Use Win_Redraw function as a Handler. Attach will call it with parent's handle as argument.
Attach(h, "p r2") ;Pin control with delayed refreshing.
; This is how to do delayed refresh of entire window.
; To prevent redraw spam which can be annoying in some cases,
; you can choose to redraw entire window only when user has finished resizing it.
; This is similar to r2 option for controls, except it works with entire parent.
Attach("OnAttach") ;Set Handler to OnAttach function
...
OnAttach( Hwnd ) {
global hGuiToRedraw := hwnd
SetTimer, Redraw, -100
}
Redraw:
Win_Redraw(hGuiToRedraw)
return
(end code)
Working sample:
(start code)
#SingleInstance, force
Gui, +Resize
Gui, Add, Edit, HWNDhe1 w150 h100
Gui, Add, Picture, HWNDhe2 w100 x+5 h100, pic.bmp
Gui, Add, Edit, HWNDhe3 w100 xm h100
Gui, Add, Edit, HWNDhe4 w100 x+5 h100
Gui, Add, Edit, HWNDhe5 w100 yp x+5 h100
gosub SetAttach ;comment this line to disable Attach
Gui, Show, autosize
return
SetAttach:
Attach(he1, "w.5 h")
Attach(he2, "x.5 w.5 h r")
Attach(he3, "y w1/3")
Attach(he4, "y x1/3 w1/3")
Attach(he5, "y x2/3 w1/3")
return
(end code)
About:
o 1.1 by majkinetor
o Licensed under BSD <http://creativecommons.org/licenses/BSD/>
*/
Attach(hCtrl="", aDef="") {
Attach_(hCtrl, aDef, "", "")
}
Attach_(hCtrl, aDef, Msg, hParent){
static
static Handler:="",adrWindowInfo:="",adrSetWindowPos:=""
local s1,s2,f,k, enable:=0, reset:=0, oldCritical
if (aDef = "") { ;Reset if integer, Handler if string
if IsFunc(hCtrl)
return Handler := hCtrl
ifEqual, adrWindowInfo,, return ;Resetting prior to adding any control just returns.
hParent := hCtrl != "" ? hCtrl+0 : hGui
loop, parse, %hParent%a, %A_Space%
{
hCtrl := A_LoopField, SubStr(%hCtrl%,1,1), aDef := SubStr(%hCtrl%,1,1)="-" ? SubStr(%hCtrl%,2) : %hCtrl%, %hCtrl% := ""
gosub Attach_GetPos
loop, parse, aDef, %A_Space%
{
StringSplit, z, A_LoopField, :
%hCtrl% .= A_LoopField="r" ? "r " : (z1 ":" z2 ":" c%z1% " ")
}
%hCtrl% := SubStr(%hCtrl%, 1, -1)
}
reset := 1, %hParent%_s := %hParent%_pw " " %hParent%_ph
}
if (hParent = "") { ;Initialize controls
if !adrSetWindowPos
adrSetWindowPos := DllCall("GetProcAddress", "ptr", DllCall("GetModuleHandle", "str", "user32"), A_IsUnicode ? "astr" : "str", "SetWindowPos","ptr")
,adrWindowInfo := DllCall("GetProcAddress", "ptr", DllCall("GetModuleHandle", "str", "user32"), A_IsUnicode ? "astr" : "str", "GetWindowInfo","ptr")
,OnMessage(5, A_ThisFunc), VarSetCapacity(B, 60), NumPut(60, B), adrB := &B
,hGui := DllCall("GetParent", "ptr", hCtrl, "ptr")
hParent := DllCall("GetParent", "ptr", hCtrl, "ptr")
if !%hParent%_s
DllCall(adrWindowInfo, "ptr", hParent, "ptr", adrB), %hParent%_pw := NumGet(B, 28,"UInt") - NumGet(B, 20,"UInt"), %hParent%_ph := NumGet(B, 32,"UInt") - NumGet(B, 24,"UInt"), %hParent%_s := !%hParent%_pw || !%hParent%_ph ? "" : %hParent%_pw " " %hParent%_ph
if InStr(" " aDef " ", "p")
StringReplace, aDef, aDef, p, xp yp wp hp
ifEqual, aDef, -, return SubStr(%hCtrl%,1,1) != "-" ? %hCtrl% := "-" %hCtrl% :
else if (aDef = "+")
if SubStr(%hCtrl%,1,1) != "-"
return
else %hCtrl% := SubStr(%hCtrl%, 2), enable := 1
else {
gosub Attach_GetPos
%hCtrl% := ""
loop, parse, aDef, %A_Space%
{
if (l := A_LoopField) = "-" {
%hCtrl% := "-" %hCtrl%
continue
}
f := SubStr(l,1,1), k := StrLen(l)=1 ? 1 : SubStr(l,2)
if (j := InStr(l, "/"))
k := SubStr(l, 2, j-2) / SubStr(l, j+1)
%hCtrl% .= f ":" k ":" c%f% " "
}
return %hCtrl% := SubStr(%hCtrl%, 1, -1), %hParent%a .= InStr(%hParent%, hCtrl) ? "" : (%hParent%a = "" ? "" : " ") hCtrl
}
}
ifEqual, %hParent%a,, return ;return if nothing to anchor.
if !reset && !enable {
%hParent%_pw := aDef & 0xFFFF, %hParent%_ph := aDef >> 16
ifEqual, %hParent%_ph, 0, return ;when u create gui without any control, it will send message with height=0 and scramble the controls ....
}
if !%hParent%_s
%hParent%_s := %hParent%_pw " " %hParent%_ph
oldCritical := A_IsCritical
critical, 5000
StringSplit, s, %hParent%_s, %A_Space%
loop, parse, %hParent%a, %A_Space%
{
hCtrl := A_LoopField, aDef := %hCtrl%, uw := uh := ux := uy := r := 0, hCtrl1 := SubStr(%hCtrl%,1,1)
if (hCtrl1 = "-")
ifEqual, reset,, continue
else aDef := SubStr(aDef, 2)
gosub Attach_GetPos
loop, parse, aDef, %A_Space%
{
StringSplit, z, A_LoopField, : ; opt:coef:initial
ifEqual, z1, r, SetEnv, r, %z2%
if z2=p
c%z1% := z3 * (z1="x" || z1="w" ? %hParent%_pw/s1 : %hParent%_ph/s2), u%z1% := true
else c%z1% := z3 + z2*(z1="x" || z1="w" ? %hParent%_pw-s1 : %hParent%_ph-s2), u%z1% := true
}
flag := 4 | (r=1 ? 0x100 : 0) | (uw OR uh ? 0 : 1) | (ux OR uy ? 0 : 2) ; nozorder=4 nocopybits=0x100 SWP_NOSIZE=1 SWP_NOMOVE=2
;m(hParent, %hParent%a, hCtrl, %hCTRL%)
DllCall(adrSetWindowPos, "ptr", hCtrl, "ptr", 0, "uint", cx, "uint", cy, "uint", cw, "uint", ch, "uint", flag)
r+0=2 ? Attach_redrawDelayed(hCtrl) :
}
critical %oldCritical%
return Handler != "" ? %Handler%(hParent) : ""
Attach_GetPos: ;hParent & hCtrl must be set up at this point
DllCall(adrWindowInfo, "ptr", hParent, "ptr", adrB), lx := NumGet(B, 20,"UInt"), ly := NumGet(B, 24,"UInt"), DllCall(adrWindowInfo, "ptr", hCtrl, "ptr", adrB)
,cx :=NumGet(B, 4,"UInt"), cy := NumGet(B, 8,"UInt"), cw := NumGet(B, 12,"UInt")-cx, ch := NumGet(B, 16,"UInt")-cy, cx-=lx, cy-=ly
return
}
Attach_redrawDelayed(hCtrl){
static s
s .= !InStr(s, hCtrl) ? hCtrl " " : ""
SetTimer, %A_ThisFunc%, -100
return
Attach_redrawDelayed:
loop, parse, s, %A_Space%
WinSet, Redraw, , ahk_id %A_LoopField%
s := ""
return
}
; LV_SortArrow by Solar. http://www.autohotkey.com/forum/viewtopic.php?t=69642
; h = ListView handle
; c = 1 based index of the column
; d = Optional direction to set the arrow. "asc" or "up". "desc" or "down".
; #Include <_Struct>
LV_SortArrow(h, i,d=""){
static C:=new _Struct("UINT mask,int fmt,int cx,LPTSTR pszText,int cchTextMax,int iSubItem,int iImage,int iOrder,int cxMin,int cxDefault,int cxIdeal")
static LVM_GETCOLUMN:=(A_IsUnicode?0x105f:0x1019),LVM_GETHEADER:=0x101f,HDM_GETITEMCOUNT:=0x1200,LVM_SETCOLUMN:=(A_IsUnicode?0x1060:0x101a)
i -= 1 ; convert to 0 based index
C.mask:=1,DllCall("SendMessage","UPTR",h,"uint",LVM_GETCOLUMN,"UPTR",i,"UPTR",C[""])
If ((fmt:=C.fmt)&1024){
If (d && d = "asc" || d = "up")
Return
C.fmt:=fmt&~1024|512,DllCall("SendMessage","UPTR",h,"uint",LVM_SETCOLUMN,"UPTR",i,"UPTR",C[""])
} else if (fmt&512){
If (d && d = "desc" || d = "down")
Return
C.fmt:=fmt&~512|1024,DllCall("SendMessage","UPTR",h,"uint",LVM_SETCOLUMN,"UPTR",i,"UPTR",C[""])
} else { ; no arrow set. check and remove arrow on other columns
Loop % DllCall("SendMessage","UPTR",DllCall("SendMessage","UPTR",h,"uint",LVM_GETHEADER,"UPTR",0,"UPTR",0,"UPTR")
,"uint",HDM_GETITEMCOUNT,"UPTR",0,"UPTR",0,"UPTR")
If (A_Index - 1 != c) ; skip our new column that we already checked.
DllCall("SendMessage","UPTR",h,"uint",LVM_GETCOLUMN,"UPTR",A_Index-1,"UPTR",C[""])
,C.fmt:=C.fmt & ~1536
,DllCall("SendMessage","UPTR",h,"uint",LVM_SETCOLUMN,"UPTR",A_Index-1,"UPTR",C[""])
C.fmt:=C.fmt|(d && d = "desc" || d = "down" ? 512 : 1024),DllCall("SendMessage","UPTR",h,"uint",LVM_SETCOLUMN,"UPTR",i,"UPTR",C[""])
}
}
; ======================================================================================================================
; Namespace: LV_InCellEdit
; AHK version: AHK 1.1.05.+ (required)
; Function: Helper object and functions for in-cell ListView editing
; Language: English
; Tested on: Win XPSP3 (ANSI), Win VistaSP2 (U32), Win 7 (U64)
; Version: 1.1.01.00/2012-03-18/just me
; 1.1.02.00/2012-05-01/just me - Added method SetColumns
; 1.1.03.00/2012-05-05/just me - Added back option BlankSubItem for method Attach
; ======================================================================================================================
; CLASS LV_InCellEdit
;
; Unlike all other in-cell editing scripts, this class is using the ListViews built-in edit control. Its advantage
; is that you don't have to care about the font and the GUI, and most of the job can be done by handling common
; ListView notifications.
; On the other hand the ListViews must be subclassed while editing. Furthermore I've still found no way to prevent them
; from blanking out the first subitem of the row while editing another subitem. The only known workaround is to add a
; hidden first column.
;
; The class provides four public methods to register / unregister in-cell editing for ListView controls, to restrict
; editing to certain columns, and to register / unregister the provided message handler function for WM_NOTIFY messages
; (see below).
;
; If a ListView is registered for in-cell editing a doubleclick on any cell will show an Edit control within this cell
; allowing to edit the current content. The default behavior for editing the first column by two subsequent single
; clicks is disabled. You have to press "Esc" to cancel editing, otherwise the changed content of the Edit will be
; stored in the cell. ListViews must have the -ReadOnly option to be editable.
;
; While editing, "Esc", "Tab", "Shift+Tab", "Down", and "Up" keys are registered as hotkeys. Esc will cancel editing
; without changing the value of the current cell. All other hotkeys will store the changed content of the edit in the
; current cell and continue editing for the next (Tab), previous (Shift+Tab), upper (Up), or lower (Down) cell. You
; must not use this hotkeys for other purposes while editing.
;
; All changes are stored in LV_InCellEdit.Changed per HWND. You may track the changes by triggering (A_GuiEvent == "F")
; in the ListViews gLabels and checking LV_InCellEdit.Changed.HasKey(ListViewHWND) as shown in the sample scipt.
; If "True", LV_InCellEdit.Changed[ListViewHWND] contains an array of objects with keys "Row" (1-based row number),
; "Col" (1-based column number), and "Txt" (new content). LV_InCellEdit.Changed is the one and only key intended to be
; accessed directly from outside the class.
;
; If you want to use the provided message handler you must call LV_InCellEdit.OnMessage() once before editing any
; controls. Otherwise you should integrate the code within LV_InCellEdit_WM_NOTIFY into your own notification handler.
; Without the notification handling editing won't work.
; ======================================================================================================================
LV_InCellEdit(){
LV_InCellEdit.OnMessage()
}
Class LV_InCellEdit {
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; PRIVATE PROPERTIES ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Static Attached := {}
Static Changed := {}
Static LVSubClassProc := RegisterCallback("LV_InCellEdit_LVSUBCLASSPROC", "", 6)
Static LVIF_TEXT := 0x0001
Static LVM_GETITEMTEXT := A_IsUnicode ? 0x1073 : 0x102D ; LVM_GETITEMTEXTW : LVM_GETITEMTEXTA
Static LVM_SETITEMTEXT := A_IsUnicode ? 0x1074 : 0x102E ; LVM_SETITEMTEXTW : LVM_SETITEMTEXTA
Static LVM_SETITEM := A_IsUnicode ? 0x104C : 0x1006 ; LVM_SETITEMW : LVM_SETITEMA
Static NMHDRSize := (2 * A_PtrSize) + 4 + (A_PtrSize - 4) ; Size off NMHDR structure
Static LVITEMSize := (13 * 4) + (A_PtrSize * 2) + (A_PtrSize - 4) ; Size off LVITEM
Static ITEMTextP := (5 * 4) + (A_PtrSize - 4) ; Offset of pszText in LVITEM
Static ITEMTextL := LV_InCellEdit.ITEMTextP + A_PtrSize ; Offset of cchTextMax in LVITEM
Static OSVersion := LV_InCellEdit.GetOsVersion()
; Hotkeys
Static HK_Esc := 0x801B ; Esc : cancel
Static HK_Right := 0x8009 ; Tab : next cell
Static HK_Left := 0x8409 ; Shift+Tab : previous cell
Static HK_Down := 0x8028 ; Down : lower cell
Static HK_Up := 0x8026 ; Up : upper cell
; Current values
Static ITEM := -1
Static SITEM := -1
Static ITEMTEXT := ""
Static HWND := 0
Static HEDIT := 0
Static Rows := 0
Static Cols := 0
Static Cancelled := False
Static Next := False
Static DW := 0
Static EX := 0
Static EY := 0
Static EW := 0
Static EH := 0
Static LX := 0
Static LY := 0
Static LR := 0
Static LW := 0
Static SW := 0
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; META FUNCTIONS ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
__New(P*) {
Return False ; There is no reason to instantiate this class.
}
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; PRIVATE METHODS +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; ===================================================================================================================
; GetOSVersion
; ===================================================================================================================
GetOsVersion() {
Return (V := DllCall("Kernel32.dll\GetVersion", "UInt") & 0xFF) . "." . ((V >> 8) & 0xFF)
}
; ===================================================================================================================
; Next subItem
; ===================================================================================================================
NextSubItem(H, K) {
Static LVM_ENSUREVISIBLE := 0x1013
Static LVM_GETCOLUMNWIDTH := 0x101D
Static LVM_GETSUBITEMRECT := 0x1038
Static LVM_SCROLL := 0x1014
Static WM_LBUTTONDOWN := 0x0201
Static WM_LBUTTONUP := 0x0202
Static WM_LBUTTONDBLCLK := 0x0203
; OutputDebug, NextSubItem
; Find the next subitem
ITEM := This.ITEM
SITEM := This.SITEM
If (K = This.HK_Right) {
SITEM++
} Else If (K = This.HK_Left) {
SITEM--
If (SITEM = 0) && This.Attached[H].Skip0 {
SITEM--
}
} Else If (K = This.HK_Down) {
ITEM++
} Else If (K = This.HK_Up) {
ITEM--
}
IF (K = This.HK_Left) || (K = This.HK_Right) {
If This.Attached[H].HasKey("Columns") {
If (SITEM < This.Attached[H].Columns.MinIndex()) {
SITEM := This.Attached[H].Columns.MaxIndex()
ITEM--
} Else If (SITEM > This.Attached[H].Columns.MaxIndex()) {
SITEM := This.Attached[H].Columns.MinIndex()
ITEM++
} Else {
While !This.Attached[H].Columns.HasKey(SITEM) {
If (K = This.HK_Right)
SITEM++
Else
SITEM--
}
}
}
}
If (SITEM > This.Cols) {
ITEM++
SITEM := This.Attached[H].Skip0 ? 1 : 0
} Else If (SITEM < 0) {
SITEM := This.Cols
ITEM--
}
If (ITEM > This.Rows) {
ITEM := 0
} Else If (ITEM < 0) {
ITEM := This.Rows
}
If (ITEM <> This.ITEM) {
SendMessage, LVM_ENSUREVISIBLE, ITEM, False, , % "ahk_id " . H
}
VarSetCapacity(RECT, 16, 0)
NumPut(SITEM, RECT, 4, "Int")
SendMessage, LVM_GETSUBITEMRECT, ITEM, &RECT, , % "ahk_id " . H
X := NumGet(RECT, 0, "Int")
Y := NumGet(RECT, 4, "Int")
If (SITEM = 0) {
SendMessage, LVM_GETCOLUMNWIDTH, 0, 0, , % "ahk_id " . H
W := ErrorLevel
} Else {
W := NumGet(RECT, 8, "Int") - NumGet(RECT, 0, "Int")
}
R := This.LW - (This.DX * 2) - This.SW
S := 0
If (X < 0) {
S := X
} Else If ((X + W) > R) {
S := X + W - R + This.DX
}
If (S) {
SendMessage, LVM_SCROLL, S, 0, , % "ahk_id " . H
}
Point := (X - S + (This.DX * 2)) + ((Y + (This.DY * 2)) << 16)
SendMessage, WM_LBUTTONDOWN, 0, Point, , % "ahk_id " . H
SendMessage, WM_LBUTTONUP, 0, Point, , % "ahk_id " . H
SendMessage, WM_LBUTTONDBLCLK, 0, Point, , % "ahk_id " . H
SendMessage, WM_LBUTTONUP, 0, Point, , % "ahk_id " . H
}
; ===================================================================================================================
; Register/UnRegister hotkeys
; ===================================================================================================================
RegisterHotkeys(H, Register = True) {
; MOD_SHIFT := 0x0004
If (Register) { ; Register
DllCall("User32.dll\RegisterHotKey", "Ptr", H, "Int", This.HK_Esc, "UInt", 0, "UInt", 0x1B)
DllCall("User32.dll\RegisterHotKey", "Ptr", H, "Int", This.HK_Right, "UInt", 0, "UInt", 0x09)
DllCall("User32.dll\RegisterHotKey", "Ptr", H, "Int", This.HK_Left, "UInt", 4, "UInt", 0x09)
DllCall("User32.dll\RegisterHotKey", "Ptr", H, "Int", This.HK_Down, "UInt", 0, "UInt", 0x28)
DllCall("User32.dll\RegisterHotKey", "Ptr", H, "Int", This.HK_Up, "UInt", 0, "UInt", 0x26)
} Else { ; Unregister
DllCall("User32.dll\UnregisterHotKey", "Ptr", H, "Int", This.HK_Esc)
DllCall("User32.dll\UnregisterHotKey", "Ptr", H, "Int", This.HK_Right)
DllCall("User32.dll\UnregisterHotKey", "Ptr", H, "Int", This.HK_Left)
DllCall("User32.dll\UnregisterHotKey", "Ptr", H, "Int", This.HK_Down)
DllCall("User32.dll\UnregisterHotKey", "Ptr", H, "Int", This.HK_Up)
}
}
; ===================================================================================================================
; LVN_BEGINLABELEDIT notification
; ===================================================================================================================
On_LVN_BEGINLABELEDIT(H, L) {
Static LVM_GETCOLUMNWIDTH := 0x101D
Static LVM_GETEDITCONTROL := 0x1018
Static LVM_GETSUBITEMRECT := 0x1038
Static TBufSize := A_IsUnicode ? 2048 : 1024
Static Indent := 4 ; indent of the Edit control, 4 seems to be reasonable for XP, Vista, and 7
; OutputDebug, % "BEGINLABELEDIT - " . This.Next
If (This.ITEM = -1) || (This.SITEM = -1)
Return True
SendMessage, LVM_GETEDITCONTROL, 0, 0, , % "ahk_id " . H
This.HEDIT := ErrorLevel
VarSetCapacity(ITEMTEXT, TBufSize, 0)
VarSetCapacity(LVITEM, This.LVITEMSize, 0)
NumPut(This.ITEM, LVITEM, 4, "Int")
NumPut(This.SITEM, LVITEM, 8, "Int")
NumPut(&ITEMTEXT, LVITEM, This.ITEMTextP, "Ptr")
NumPut(1024 + 1, LVITEM, This.ITEMTextL, "Int")
SendMessage, This.LVM_GETITEMTEXT, This.ITEM, &LVITEM, , % "ahk_id " . H
This.ITEMTEXT := StrGet(&ITEMTEXT, ErrorLevel)
ControlSetText, , % This.ITEMTEXT, % "ahk_id " . This.HEDIT
If (This.SITEM > 0) && (This.Attached[H].Blank) {
Empty := ""
NumPut(&Empty, LVITEM, This.ITEMTextP, "Ptr")
NumPut(0,LVITEM, This.ITEMTextL, "Int")
SendMessage, This.LVM_SETITEMTEXT, This.ITEM, &LVITEM, , % "ahk_id " . H
}
VarSetCapacity(RECT, 16, 0)
NumPut(This.SITEM, RECT, 4, "Int")
SendMessage, LVM_GETSUBITEMRECT, This.ITEM, &RECT, , % "ahk_id " . H
This.EX := NumGet(RECT, 0, "Int") + This.LX + This.DX + Indent
This.EY := NumGet(RECT, 4, "Int") + This.LY + This.DY
If (This.OSVersion < 6)
This.EY -= 1 ; subtract 1 for WinXP
If (This.SITEM = 0) {
SendMessage, LVM_GETCOLUMNWIDTH, 0, 0, , % "ahk_id " . H
This.EW := ErrorLevel
} Else {
This.EW := NumGet(RECT, 8, "Int") - NumGet(RECT, 0, "Int")
}
This.EW -= Indent
This.EH := NumGet(RECT, 12, "Int") - NumGet(RECT, 4, "Int")
; Register subclass callback
DllCall("Comctl32.dll\SetWindowSubclass", "Ptr", H, "Ptr", This.LVSubClassProc, "Ptr", H, "Ptr", 0)
; Register hotkeys
If !(This.Next) {
This.RegisterHotkeys(H)
}
This.Cancelled := False
This.Next := False
Return False
}
; ===================================================================================================================
; LVN_ENDLABELEDIT notification
; ===================================================================================================================
On_LVN_ENDLABELEDIT(H, L) {
; OutputDebug, % "ENDLABELEDIT - " . This.Next
; Unregister subclass callback
DllCall("Comctl32.dll\RemoveWindowSubclass", "Ptr", H, "Ptr", This.LVSubClassProc, "Ptr", H)
; Unregister hotkeys
If !(This.Next) {
This.RegisterHotkeys(H, False)
}
ITEMTEXT := This.ITEMTEXT
If !(This.Cancelled) {
ControlGetText, ITEMTEXT, , % "ahk_id " . This.HEDIT
}
If (ITEMTEXT <> This.ITEMTEXT) {
If !(This.Changed.HasKey(H))
This.Changed[H] := []
This.Changed[H].Insert({Row: This.ITEM + 1, Col: This.SITEM + 1, Txt: ITEMTEXT})
}
; Restore subitem's text if changed or blanked out
If (ITEMTEXT <> This.ITEMTEXT) || ((This.SITEM > 0) && (This.Attached[H].Blank)) {
VarSetCapacity(LVITEM, This.LVITEMSize, 0)
NumPut(This.ITEM, LVITEM, 4, "Int")
NumPut(This.SITEM, LVITEM, 8, "Int")
NumPut(&ITEMTEXT, LVITEM, This.ITEMTextP, "Ptr")
SendMessage, This.LVM_SETITEMTEXT, This.ITEM, &LVITEM, , % "ahk_id " . H
}
If !(This.Next) {
This.ITEM := This.SITEM := -1
}
This.Cancelled := False
This.Next := False
Return False
}
; ===================================================================================================================
; NM_DBLCLICK notification
; ===================================================================================================================
On_NM_DBLCLICK(H, L) {
Static LVM_EDITLABELA := 0x1017
Static LVM_EDITLABELW := 0x1076
Static LVM_EDITLABEL := A_IsUnicode ? LVM_EDITLABELW : LVM_EDITLABELA
Static LVM_GETCOLUMNWIDTH := 0x101D
Static LVM_GETSTRINGWIDTHA := 0x1011
Static LVM_GETSTRINGWIDTHW := 0x1057
Static LVM_GETSTRINGWIDTH := A_IsUnicode ? LVM_GETSTRINGWIDTHW : LVM_GETSTRINGWIDTHA
Static LVM_GETSUBITEMRECT := 0x1038
Static LVM_SCROLL := 0x1014
Static WS_VSCROLL := 0x200000
; OutputDebug, % "NMDBLCLICK - " . This.Next
This.ITEM := This.SITEM := -1
ITEM := NumGet(L + This.NMHDRSize, 0, "Int")
SITEM := NumGet(L + This.NMHDRSize, 4, "Int")
If This.Attached[H].HasKey("Columns") {
If !This.Attached[H].Columns.HasKey(SITEM)
Return False
}
If (ITEM >= 0) && (SITEM >= 0) {
This.ITEM := ITEM
This.SITEM := SITEM
If !(This.Next) {
This.HWND := H
ControlGet, V, List, Count, , % "ahk_id " . H
This.Rows := V - 1
ControlGet, V, List, Count Col, , % "ahk_id " . H
This.Cols := V - 1
NumPut(VarSetCapacity(WINDOWINFO, 60, 0), WINDOWINFO)
DllCall("User32.dll\GetWindowInfo", "Ptr", H, "Ptr", &WINDOWINFO)
This.DX := NumGet(WINDOWINFO, 20, "Int") - NumGet(WINDOWINFO, 4, "Int")
This.DY := NumGet(WINDOWINFO, 24, "Int") - NumGet(WINDOWINFO, 8, "Int")
Styles := NumGet(WINDOWINFO, 36, "UInt")
SendMessage, LVM_GETSTRINGWIDTH, 0, "WWW", , % "ahk_id " . H
This.MinW := ErrorLevel
SendMessage, LVM_GETSTRINGWIDTH, 0, "III", , % "ahk_id " . H
This.DW := ErrorLevel
SBW := 0
If (Styles & WS_VSCROLL)
SysGet, SBW, 2
ControlGetPos, LX, LY, LW, , , % "ahk_id " . H
This.LX := LX
This.LY := LY
This.LR := LX + LW - (This.DX * 2) - SBW
This.LW := LW
This.SW := SBW
VarSetCapacity(RECT, 16, 0)
NumPut(SITEM, RECT, 4, "Int")
SendMessage, LVM_GETSUBITEMRECT, ITEM, &RECT, , % "ahk_id " . H
X := NumGet(RECT, 0, "Int")
If (SITEM = 0) {
SendMessage, LVM_GETCOLUMNWIDTH, 0, 0, , % "ahk_id " . H
W := ErrorLevel
} Else {
W := NumGet(RECT, 8, "Int") - NumGet(RECT, 0, "Int")
}
R := LW - (This.DX * 2) - SBW
If (X < 0) {
SendMessage, LVM_SCROLL, X, 0, , % "ahk_id " . H
} Else If ((X + W) > R) {
SendMessage, LVM_SCROLL, X + W - R + This.DX, 0, , % "ahk_id " . H
}
}
PostMessage, LVM_EDITLABEL, ITEM, 0, , % "ahk_id " . H
}
Return False
}
; ===================================================================================================================
; SubClassProc for ListViews
; ===================================================================================================================
SubClassProc(H, M, W, L, I, D) {
Static EC_LEFTMARGIN := 0x01
Static EN_SETFOCUS := 0x0100
Static EN_KILLFOCUS := 0x0200
Static EN_CHANGE := 0x0300
Static EN_UPDATE := 0x0400
Static EM_SETSEL := 0x00B1
Static EM_SETMARGINS := 0x00D3
Static WM_COMMAND := 0x0111
Static WM_HOTKEY := 0x0312
Static LVM_CANCELEDITLABEL := 0x10B3
Static LVM_GETSTRINGWIDTHA := 0x1011
Static LVM_GETSTRINGWIDTHW := 0x1057
Static LVM_GETSTRINGWIDTH := A_IsUnicode ? LVM_GETSTRINGWIDTHW : LVM_GETSTRINGWIDTHA
If (H = This.HWND) {
If (L = This.HEDIT) && (M = WM_COMMAND) {
N := (W >> 16)
If (N = EN_UPDATE) || (N = EN_CHANGE) || (N = EN_SETFOCUS) {
; OutputDebug, SubClassProc Edit : %N%
If (N = EN_SETFOCUS)
SendMessage, EM_SETMARGINS, EC_LEFTMARGIN, 0, , % "ahk_id " . L
ControlGetText, EDITTEXT, , % "ahk_id " . L
SendMessage, LVM_GETSTRINGWIDTH, 0, &EDITTEXT, , % "ahk_id " . H
EW := ErrorLevel + This.DW
If (EW < This.MinW)
EW := This.MinW
EX := This.EX
EY := This.EY
EH := This.OSVersion < 6 ? This.EH + 3 : This.EH ; add 3 for WinXP
If ((EX + EW) > This.LR) {
EW := This.LR - EX
}
ControlMove, , EX, EY, EW, EH, % "ahk_id " . L
If (N = EN_UPDATE)
Return 0
}
} Else If (M = WM_HOTKEY) {
; OutputDebug, SubClassProc Hotkey : %W%
If (W = This.HK_Esc) {
This.Cancelled := True
PostMessage, LVM_CANCELEDITLABEL, 0, 0, , % "ahk_id " . H
} Else {
This.Next := True
SendMessage, LVM_CANCELEDITLABEL, 0, 0, , % "ahk_id " . H
This.Next := True
This.NextSubItem(H, W)
}
Return False
}
}
Return DllCall("Comctl32.dll\DefSubclassProc", "Ptr", H, "UInt", M, "Ptr", W, "Ptr", L)
}
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; PUBLIC INTERFACE ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; ===================================================================================================================
; METHOD OnMessage Register / unregister LV_InCellEdit message handler for WM_NOTIFY messages
; Parameters: DoIt - True / False
; Default: True
; Return Value: Always True
; ===================================================================================================================
OnMessage(DoIt = True) {
Static MessageHandler := "LV_InCellEdit_WM_NOTIFY"
Static WM_NOTIFY := 0x4E
If (DoIt) {
OnMessage(WM_NOTIFY, MessageHandler)
} Else If (MessageHandler = OnMessage(WM_NOTIFY)) {
OnMessage(WM_NOTIFY, "")
}
Return True
}
; ===================================================================================================================
; METHOD Attach Register ListView for in-cell editing
; Parameters: HWND - ListView's HWND (Integer)
; ---------------- Optional:
; HiddenCol1 - ListView with hidden first column (Bool)
; Values: True / False
; Default: False
; BlankSubItem - Blank out subitem's text while editing (Bool)
; Values: True / False
; Default: False
; Return values: Always True
; ===================================================================================================================
Attach(HWND, HiddenCol1 = False, BlankSubItem = False) {
; Store HWND and options
This.Attached[HWND] := {Skip0: HiddenCol1, Blank: BlankSubItem}
Return True
}
; ===================================================================================================================
; METHOD Detach Restore ListView's default behavior
; Parameters: HWND - ListView's HWND (Integer)
; Return values: Always True
; ===================================================================================================================
Detach(HWND) {
; Remove HWND
This.Attached.Remove(HWND, "")
Return True
}
; ===================================================================================================================
; METHOD SetColumns Sets the columns you want to edit
; Parameters: HWND - ListView's HWND (Integer)
; ColArr - Array of 1-based column indices (Array)
; If a non-array is passed (e.g. the parameter is omitted),
; the ListView will be resetted to edit all columns.
; Return values: On success: True
; On failure: False
; ===================================================================================================================
SetColumns(HWND, ColArr = "") {
If !This.Attached.HasKey(HWND)
Return False
If !IsObject(ColArr) {
This.Attached[HWND].Remove("Columns")
Return True
}
ControlGet, Cols, List, Count Col, , % "ahk_id " . HWND
Indices := []
For Each, I In ColArr {
If I Is Not Integer
Return False
If (I < 1) || (I > Cols)
Return False
Indices[I - 1] := True
}
This.Attached[HWND, "Columns"] := Indices
Return True
}
}
; ======================================================================================================================
; PRIVATE ListView subclassproc
; ======================================================================================================================
LV_InCellEdit_LVSUBCLASSPROC(H, M, W, L, I, D) {
; Critical
Return LV_InCellEdit.SubClassProc(H, M, W, L, I, D)
}
; ======================================================================================================================
; PRIVATE ListView notification handler
; ======================================================================================================================
LV_InCellEdit_WM_NOTIFY(W, L) {
Static LVN_BEGINLABELEDITA := -105
Static LVN_BEGINLABELEDITW := -175
Static LVN_ENDLABELEDITA := -106
Static LVN_ENDLABELEDITW := -176
Static NM_CLICK := -2
Static NM_DBLCLICK := -3
H := NumGet(L + 0, 0, "UPtr")
M := NumGet(L + (A_PtrSize * 2), 0, "Int")
If (LV_InCellEdit.Attached.HasKey(H)) {
; BeginLabelEdit -------------------------------------------------------------------------------------------------
If (M = LVN_BEGINLABELEDITW) || (M = LVN_BEGINLABELEDITA) {
Return LV_InCellEdit.On_LVN_BEGINLABELEDIT(H, L)
}
; EndLabelEdit ---------------------------------------------------------------------------------------------------
If (M = LVN_ENDLABELEDITW) || (M = LVN_ENDLABELEDITA) {
Return LV_InCellEdit.On_LVN_ENDLABELEDIT(H, L)
}
; Double click ---------------------------------------------------------------------------------------------------
If (M = NM_DBLCLICK) {
LV_InCellEdit.On_NM_DBLCLICK(H, L)
}
}
}
; ======================================================================================================================
Class _Struct {
; Data Sizes
static PTR:=A_PtrSize,UPTR:=A_PtrSize,SHORT:=2,USHORT:=2,INT:=4,UINT:=4,__int64:=8,INT64:=8,UINT64:=8,DOUBLE:=8,FLOAT:=4,CHAR:=1,UCHAR:=1,VOID:=A_PtrSize
,TBYTE:=A_IsUnicode?2:1,TCHAR:=A_IsUnicode?2:1,HALF_PTR:=A_PtrSize=8?4:2,UHALF_PTR:=A_PtrSize=8?4:2,INT32:=4,LONG:=4,LONG32:=4,LONGLONG:=8
,LONG64:=8,USN:=8,HFILE:=4,HRESULT:=4,INT_PTR:=A_PtrSize,LONG_PTR:=A_PtrSize,POINTER_64:=A_PtrSize,POINTER_SIGNED:=A_PtrSize
,BOOL:=4,SSIZE_T:=A_PtrSize,WPARAM:=A_PtrSize,BOOLEAN:=1,BYTE:=1,COLORREF:=4,DWORD:=4,DWORD32:=4,LCID:=4,LCTYPE:=4,LGRPID:=4,LRESULT:=4,PBOOL:=4
,PBOOLEAN:=A_PtrSize,PBYTE:=A_PtrSize,PCHAR:=A_PtrSize,PCSTR:=A_PtrSize,PCTSTR:=A_PtrSize,PCWSTR:=A_PtrSize,PDWORD:=A_PtrSize,PDWORDLONG:=A_PtrSize
,PDWORD_PTR:=A_PtrSize,PDWORD32:=A_PtrSize,PDWORD64:=A_PtrSize,PFLOAT:=A_PtrSize,PHALF_PTR:=A_PtrSize
,UINT32:=4,ULONG:=4,ULONG32:=4,DWORDLONG:=8,DWORD64:=8,ULONGLONG:=8,ULONG64:=8,DWORD_PTR:=A_PtrSize,HACCEL:=A_PtrSize,HANDLE:=A_PtrSize
,HBITMAP:=A_PtrSize,HBRUSH:=A_PtrSize,HCOLORSPACE:=A_PtrSize,HCONV:=A_PtrSize,HCONVLIST:=A_PtrSize,HCURSOR:=A_PtrSize,HDC:=A_PtrSize
,HDDEDATA:=A_PtrSize,HDESK:=A_PtrSize,HDROP:=A_PtrSize,HDWP:=A_PtrSize,HENHMETAFILE:=A_PtrSize,HFONT:=A_PtrSize
static HGDIOBJ:=A_PtrSize,HGLOBAL:=A_PtrSize,HHOOK:=A_PtrSize,HICON:=A_PtrSize,HINSTANCE:=A_PtrSize,HKEY:=A_PtrSize,HKL:=A_PtrSize
,HLOCAL:=A_PtrSize,HMENU:=A_PtrSize,HMETAFILE:=A_PtrSize,HMODULE:=A_PtrSize,HMONITOR:=A_PtrSize,HPALETTE:=A_PtrSize,HPEN:=A_PtrSize
,HRGN:=A_PtrSize,HRSRC:=A_PtrSize,HSZ:=A_PtrSize,HWINSTA:=A_PtrSize,HWND:=A_PtrSize,LPARAM:=A_PtrSize,LPBOOL:=A_PtrSize,LPBYTE:=A_PtrSize
,LPCOLORREF:=A_PtrSize,LPCSTR:=A_PtrSize,LPCTSTR:=A_PtrSize,LPCVOID:=A_PtrSize,LPCWSTR:=A_PtrSize,LPDWORD:=A_PtrSize,LPHANDLE:=A_PtrSize
,LPINT:=A_PtrSize,LPLONG:=A_PtrSize,LPSTR:=A_PtrSize,LPTSTR:=A_PtrSize,LPVOID:=A_PtrSize,LPWORD:=A_PtrSize,LPWSTR:=A_PtrSize,PHANDLE:=A_PtrSize
,PHKEY:=A_PtrSize,PINT:=A_PtrSize,PINT_PTR:=A_PtrSize,PINT32:=A_PtrSize,PINT64:=A_PtrSize,PLCID:=A_PtrSize,PLONG:=A_PtrSize,PLONGLONG:=A_PtrSize
,PLONG_PTR:=A_PtrSize,PLONG32:=A_PtrSize,PLONG64:=A_PtrSize,POINTER_32:=A_PtrSize,POINTER_UNSIGNED:=A_PtrSize,PSHORT:=A_PtrSize,PSIZE_T:=A_PtrSize
,PSSIZE_T:=A_PtrSize,PSTR:=A_PtrSize,PTBYTE:=A_PtrSize,PTCHAR:=A_PtrSize,PTSTR:=A_PtrSize,PUCHAR:=A_PtrSize,PUHALF_PTR:=A_PtrSize,PUINT:=A_PtrSize
,PUINT_PTR:=A_PtrSize,PUINT32:=A_PtrSize,PUINT64:=A_PtrSize,PULONG:=A_PtrSize,PULONGLONG:=A_PtrSize,PULONG_PTR:=A_PtrSize,PULONG32:=A_PtrSize
,PULONG64:=A_PtrSize,PUSHORT:=A_PtrSize,PVOID:=A_PtrSize,PWCHAR:=A_PtrSize,PWORD:=A_PtrSize,PWSTR:=A_PtrSize,SC_HANDLE:=A_PtrSize
,SC_LOCK:=A_PtrSize,SERVICE_STATUS_HANDLE:=A_PtrSize,SIZE_T:=A_PtrSize,UINT_PTR:=A_PtrSize,ULONG_PTR:=A_PtrSize,ATOM:=2,LANGID:=2,WCHAR:=2,WORD:=2,USAGE:=2
; Data Types
static _PTR:="PTR",_UPTR:="UPTR",_SHORT:="Short",_USHORT:="UShort",_INT:="Int",_UINT:="UInt"
,_INT64:="Int64",_UINT64:="UInt64",_DOUBLE:="Double",_FLOAT:="Float",_CHAR:="Char",_UCHAR:="UChar"
,_VOID:="PTR",_TBYTE:=A_IsUnicode?"USHORT":"UCHAR",_TCHAR:=A_IsUnicode?"USHORT":"UCHAR",_HALF_PTR:=A_PtrSize=8?"INT":"SHORT"
,_UHALF_PTR:=A_PtrSize=8?"UINT":"USHORT",_BOOL:="Int",_INT32:="Int",_LONG:="Int",_LONG32:="Int",_LONGLONG:="Int64",_LONG64:="Int64"
,_USN:="Int64",_HFILE:="UInt",_HRESULT:="UInt",_INT_PTR:="PTR",_LONG_PTR:="PTR",_POINTER_64:="PTR",_POINTER_SIGNED:="PTR",_SSIZE_T:="PTR"
,_WPARAM:="PTR",_BOOLEAN:="UCHAR",_BYTE:="UCHAR",_COLORREF:="UInt",_DWORD:="UInt",_DWORD32:="UInt",_LCID:="UInt",_LCTYPE:="UInt"
,_LGRPID:="UInt",_LRESULT:="UInt",_PBOOL:="UPTR",_PBOOLEAN:="UPTR",_PBYTE:="UPTR",_PCHAR:="UPTR",_PCSTR:="UPTR",_PCTSTR:="UPTR"
,_PCWSTR:="UPTR",_PDWORD:="UPTR",_PDWORDLONG:="UPTR",_PDWORD_PTR:="UPTR",_PDWORD32:="UPTR",_PDWORD64:="UPTR",_PFLOAT:="UPTR",___int64:="Int64"
,_PHALF_PTR:="UPTR",_UINT32:="UInt",_ULONG:="UInt",_ULONG32:="UInt",_DWORDLONG:="UInt64",_DWORD64:="UInt64",_ULONGLONG:="UInt64"
,_ULONG64:="UInt64",_DWORD_PTR:="UPTR",_HACCEL:="UPTR",_HANDLE:="UPTR",_HBITMAP:="UPTR",_HBRUSH:="UPTR",_HCOLORSPACE:="UPTR"
,_HCONV:="UPTR",_HCONVLIST:="UPTR",_HCURSOR:="UPTR",_HDC:="UPTR",_HDDEDATA:="UPTR",_HDESK:="UPTR",_HDROP:="UPTR",_HDWP:="UPTR"
static _HENHMETAFILE:="UPTR",_HFONT:="UPTR",_HGDIOBJ:="UPTR",_HGLOBAL:="UPTR",_HHOOK:="UPTR",_HICON:="UPTR",_HINSTANCE:="UPTR",_HKEY:="UPTR"
,_HKL:="UPTR",_HLOCAL:="UPTR",_HMENU:="UPTR",_HMETAFILE:="UPTR",_HMODULE:="UPTR",_HMONITOR:="UPTR",_HPALETTE:="UPTR",_HPEN:="UPTR"
,_HRGN:="UPTR",_HRSRC:="UPTR",_HSZ:="UPTR",_HWINSTA:="UPTR",_HWND:="UPTR",_LPARAM:="UPTR",_LPBOOL:="UPTR",_LPBYTE:="UPTR",_LPCOLORREF:="UPTR"
,_LPCSTR:="UPTR",_LPCTSTR:="UPTR",_LPCVOID:="UPTR",_LPCWSTR:="UPTR",_LPDWORD:="UPTR",_LPHANDLE:="UPTR",_LPINT:="UPTR",_LPLONG:="UPTR"
,_LPSTR:="UPTR",_LPTSTR:="UPTR",_LPVOID:="UPTR",_LPWORD:="UPTR",_LPWSTR:="UPTR",_PHANDLE:="UPTR",_PHKEY:="UPTR",_PINT:="UPTR"
,_PINT_PTR:="UPTR",_PINT32:="UPTR",_PINT64:="UPTR",_PLCID:="UPTR",_PLONG:="UPTR",_PLONGLONG:="UPTR",_PLONG_PTR:="UPTR",_PLONG32:="UPTR"
,_PLONG64:="UPTR",_POINTER_32:="UPTR",_POINTER_UNSIGNED:="UPTR",_PSHORT:="UPTR",_PSIZE_T:="UPTR",_PSSIZE_T:="UPTR",_PSTR:="UPTR"
,_PTBYTE:="UPTR",_PTCHAR:="UPTR",_PTSTR:="UPTR",_PUCHAR:="UPTR",_PUHALF_PTR:="UPTR",_PUINT:="UPTR",_PUINT_PTR:="UPTR",_PUINT32:="UPTR"
,_PUINT64:="UPTR",_PULONG:="UPTR",_PULONGLONG:="UPTR",_PULONG_PTR:="UPTR",_PULONG32:="UPTR",_PULONG64:="UPTR",_PUSHORT:="UPTR"
,_PVOID:="UPTR",_PWCHAR:="UPTR",_PWORD:="UPTR",_PWSTR:="UPTR",_SC_HANDLE:="UPTR",_SC_LOCK:="UPTR",_SERVICE_STATUS_HANDLE:="UPTR"
static _SIZE_T:="UPTR",_UINT_PTR:="UPTR",_ULONG_PTR:="UPTR",_ATOM:="Ushort",_LANGID:="Ushort",_WCHAR:="Ushort",_WORD:="UShort",_USAGE:="UShort"
; Following is used internally only to simplify setting field helpers
; the corresponding key can be set to invalid type (for string integer and vice versa) to set default if necessary, e.g. ___InitField(N,"")
___InitField(_this,N,offset=" ",encoding=0,AHKType=0,isptr=" ",type=0,arrsize=0,memory=0){ ; N = Name of field
static _prefixes_:={offset:"`b",isptr:"`r",AHKType:"`n",type:"`t",encoding:"`f",memory:"`v",arrsize:" "}
,_testtype_:={offset:"integer",isptr:"integer",AHKType:"string",type:"string",encoding:"string",arrsize:"integer"}
,_default_:={offset:0,isptr:0,AHKType:"UInt",type:"UINT",encoding:"CP0",memory:"",arrsize:1}
for _key_,_value_ in _prefixes_
{
_typevalid_:=0
If (_testtype_[_key_]="Integer"){
If %_key_% is integer
useDefault:=1,_typevalid_:=1
else if !_this.HasKey(_value_ N)
useDefault:=1
} else {
If %_key_% is not integer
useDefault:=1,_typevalid_:=1
else if !_this.HasKey(_value_ N)
useDefault:=1
}
If (useDefault) ; item does not exist or user supplied a valid type
If (_key_="encoding")
_this[_value_ N]:=_typevalid_?(InStr(",LPTSTR,LPCTSTR,TCHAR,","," %_key_% ",")?(A_IsUnicode?"UTF-16":"CP0")
:InStr(",LPWSTR,LPCWSTR,WCHAR,","," %_key_% ",")?"UTF-16":"CP0")
:_default_[_key_]
else {
_this[_value_ N]:=_typevalid_?%_key_%:_default_[_key_]
}
}
}
; Struct Contstructor
; Memory, offset and definitions are saved in following character + given key/name
; `a = Allocated Memory
; `b = Byte Offset (related to struct address)
; `f = Format (encoding for string data types)
; `n = New data type (AHK data type)
; `r = Is Pointer (requred for __GET and __SET)
; `t = Type (data type, also when it is name of a Structure it is used to resolve structure pointers dynamically
; `v = Memory used to save string and pointer memory
__NEW(_TYPE_,_pointer_=0,_init_=0){
static _base_:={__GET:_Struct.___GET,__SET:_Struct.___SET,__SETPTR:_Struct.___SETPTR,__Clone:_Struct.___Clone,__NEW:_Struct.___NEW
,IsPointer:_Struct.IsPointer,Offset:_Struct.Offset,Type:_Struct.Type,AHKType:_Struct.AHKType,Encoding:_Struct.Encoding
,Capacity:_Struct.Capacity,Alloc:_Struct.Alloc,Size:_Struct.Size,SizeT:_Struct.SizeT,Print:_Struct.Print,ToObj:_Struct.ToObj}
local _:="",_ArrType_:="",_ArrName_:="",_ArrSize_:=0,_align_total_:=0,_defobj_:="",_IsPtr_:=0,_key_:="",_LF_:="",_LF_BKP_:="",_match_:="",_offset_:=""
,_struct_:="",_StructSize_:=0,_total_union_size_:=0,_union_:=0,_union_size_:=0,_value_:="",_mod_:=0,_max_size_:=0,_in_struct_:=0,_struct_align_:=0
If (RegExMatch(_TYPE_,"^[\w\d\._]+$") && !_Struct.HasKey(_TYPE_)){ ; structures name was supplied, resolve to global var and run again
If InStr(_TYPE_,"."){ ;check for object that holds structure definition
Loop,Parse,_TYPE_,.
If A_Index=1
_defobj_:=%A_LoopField%
else _defobj_:=_defobj_[A_LoopField]
_TYPE_:=_defobj_
} else _TYPE_:=%_TYPE_%,_defobj_:=""
} else _defobj_:=""
; If a pointer is supplied, save it in key [""] else reserve and zero-fill memory + set pointer in key [""]
If (_pointer_ && !IsObject(_pointer_))
this[""] := _pointer_,this["`a"]:=0,this["`a`a"]:=sizeof(_TYPE_)
else
this._SetCapacity("`a",_StructSize_:=sizeof(_TYPE_)) ; Set Capacity in key ["`a"]
,this[""]:=this._GetAddress("`a") ; Save pointer in key [""]
,DllCall("RtlZeroMemory","UPTR",this[""],"UInt",this["`a`a"]:=_StructSize_) ; zero-fill memory
; C/C++ style structure definition, convert it
If InStr(_TYPE_,"`n") {
_struct_:=[] ; keep track of structures (union is just removed because {} = union, struct{} = struct
_union_:=0 ; init to 0, used to keep track of union depth
Loop,Parse,_TYPE_,`n,`r`t%A_Space%%A_Tab% ; Parse each line
{
_LF_:=""
Loop,Parse,A_LoopField,`,`;,`t%A_Space%%A_Tab% ; Parse each item
{
If RegExMatch(A_LoopField,"^\s*//") ;break on comments and continue main loop
break
If (A_LoopField){ ; skip empty lines
If (!_LF_ && _ArrType_:=RegExMatch(A_LoopField,"[\w\d_#@]\s+[\w\d_#@]")) ; new line, find out data type and save key in _LF_ Data type will be added later
_LF_:=RegExReplace(A_LoopField,"[\w\d_#@]\K\s+.*$")
If Instr(A_LoopField,"{"){ ; Union, also check if it is a structure
_union_++,_struct_.Insert(_union_,RegExMatch(A_LoopField,"i)^\s*struct\s*\{"))
} else If InStr(A_LoopField,"}") ; end of union/struct
_offset_.="}"
else { ; not starting or ending struct or union so add definitions and apply Data Type.
If _union_ ; add { or struct{
Loop % _union_
_ArrName_.=(_struct_[A_Index]?"struct":"") "{"
_offset_.=(_offset_ ? "," : "") _ArrName_ ((_ArrType_ && A_Index!=1)?(_LF_ " "):"") RegExReplace(A_LoopField,"\s+"," ")
,_ArrName_:="",_union_:=0
}
}
}
}
_TYPE_:=_offset_
}
_offset_:=0
,_union_:=[] ; keep track of union level, required to reset offset after union is parsed
,_struct_:=[] ; for each union level keep track if it is a structure (because here offset needs to increase
,_union_size_:=[] ; keep track of highest member within the union or structure, used to calculate new offset after union
,_struct_align_:=[] ; keep track of alignment before structure
,_total_union_size_:=0 ; used in combination with above, each loop the total offset is updated if current data size is higher
,_align_total_:=0 ; used to calculate alignment for total size of structure
,_in_struct_:=1
,this["`t"]:=0,this["`r"]:=0 ; will identify a Structure Pointer without members
; Parse given structure definition and create struct members
; User structures will be resolved by recrusive calls (!!! a structure must be a global variable)
Loop,Parse,_TYPE_,`,`; ;,%A_Space%%A_Tab%`n`r
{
_in_struct_+=StrLen(A_LoopField)+1
If ("" = _LF_ := trim(A_LoopField,A_Space A_Tab "`n`r"))
continue
_LF_BKP_:=_LF_ ;to check for ending brackets = union,struct
_IsPtr_:=0
; Check for STARTING union and set union helpers
While (_match_:=RegExMatch(_LF_,"i)^(struct|union)?\s*\{\K"))
; correct offset for union/structure, sizeof_maxsize returns max size of union or structure
_max_size_:=sizeof_maxsize(SubStr(_TYPE_,_in_struct_-StrLen(A_LoopField)-1+(StrLen(_LF_BKP_)-StrLen(_LF_))))
,_union_.Insert(_offset_+=(_mod_:=Mod(_offset_,_max_size_))?Mod(_max_size_-_mod_,_max_size_):0)
,_union_size_.Insert(0)
,_struct_align_.Insert(_align_total_>_max_size_?_align_total_:_max_size_)
,_struct_.Insert(RegExMatch(_LF_,"i)^struct\s*\{")?(1,_align_total_:=0):0)
,_LF_:=SubStr(_LF_,_match_)
StringReplace,_LF_,_LF_,},,A ;remove all closing brackets (these will be checked later)
; Check if item is a pointer and remove * for further processing, separate key will store that information
While % (InStr(_LF_,"*")){
StringReplace,_LF_,_LF_,*
_IsPtr_:=A_Index
}
; Split off data type, name and size (only data type is mandatory)
RegExMatch(_LF_,"^(?<ArrType_>[\w\d\._]+)?\s*(?<ArrName_>[\w\d_]+)?\s*\[?(?<ArrSize_>\d+)?\]?\s*\}*\s*$",_)
If (!_ArrName_ && !_ArrSize_){
If RegExMatch(_TYPE_,"^\**" _ArrType_ "\**$"){
_Struct.___InitField(this,"",0,_ArrType_,_IsPtr_?"PTR":_Struct.HasKey("_" _ArrType_)?_Struct["_" _ArrType_]:"PTR",_IsPtr_,_ArrType_)
this.base:=_base_
If (IsObject(_init_)||IsObject(_pointer_)){ ; Initialization of structures members, e.g. _Struct(_RECT,{left:10,right:20})
for _key_,_value_ in IsObject(_init_)?_init_:_pointer_
{
If !this["`r"]{ ; It is not a pointer, assign value
If InStr(",LPSTR,LPCSTR,LPTSTR,LPCTSTR,LPWSTR,LPCWSTR,","," this["`t" _key_] ",")
this.Alloc(_key_,StrLen(_value_)*(InStr(".LPWSTR,LPCWSTR,","," this["`t"] ",")||(InStr(",LPTSTR,LPCTSTR,","," this["`t" _key_] ",")&&A_IsUnicode)?2:1))
if InStr(",LPSTR,LPCSTR,LPTSTR,LPCTSTR,LPWSTR,LPCWSTR,CHAR,TCHAR,WCHAR,","," this["`t" _key_] ",")
this[_key_]:=_value_
else
this[_key_] := _value_
}else if (_value_<>"") ; It is not empty
If _value_ is integer ; It is a new pointer
this[_key_][""]:=_value_
}
}
Return this ;:= new _Struct(%_ArrType_%,_pointer_) ;only Data type was supplied, object/structure has got no members/keys
} else
_ArrName_:=_ArrType_,_ArrType_:="UInt"
}
If InStr(_ArrType_,"."){ ;check for object that holds structure definition
Loop,Parse,_ArrType_,.
If A_Index=1
_defobj_:=%A_LoopField%
else _defobj_:=_defobj_[A_LoopField]
}
if (!_IsPtr_ && !_Struct.HasKey(_ArrType_)){ ; _ArrType_ not found resolve to global variable (must contain struct definition)
if (sizeof(_defobj_?_defobj_:%_ArrType_%,0,_align_total_) && mod:=Mod(_offset_,_align_total_))
_offset_+=Mod(_align_total_-_mod_,_align_total_)
_Struct.___InitField(this,_ArrName_,_offset_,_ArrType_,0,0,_ArrType_,_ArrSize_)
; update current union size
If (_uix_:=_union_.MaxIndex()) && (_max_size_:=_offset_ + sizeof(_defobj_?_defobj_:%_ArrType_%) - _union_[_uix_])>_union_size_[_uix_]
_union_size_[_uix_]:=_max_size_
_max_size:=0
; if not a union or a union + structure then offset must be moved (when structure offset will be reset below
If (!_uix_||_struct_[_struct_.MaxIndex()])
_offset_+=this[" " _ArrName_]*sizeof(_defobj_?_defobj_:%_ArrType_%) ; move offset
;Continue
} else {
If ((_IsPtr_ || _Struct.HasKey(_ArrType_)))
_offset_+=(_mod_:=Mod(_offset_,_max_size_:=_IsPtr_?A_PtrSize:_Struct[_ArrType_]))=0?0:(_IsPtr_?A_PtrSize:_Struct[_ArrType_])-_mod_
,_align_total_:=_max_size_>_align_total_?_max_size_:_align_total_
,_Struct.___InitField(this,_ArrName_,_offset_,_ArrType_,_IsPtr_?"PTR":_Struct.HasKey(_ArrType_)?_Struct["_" _ArrType_]:_ArrType_,_IsPtr_,_ArrType_,_ArrSize_)
; update current union size
If (_uix_:=_union_.MaxIndex()) && (_max_size_:=_offset_ + _Struct[this["`n" _ArrName_]] - _union_[_uix_])>_union_size_[_uix_]
_union_size_[_uix_]:=_max_size_
_max_size_:=0
; if not a union or a union + structure then offset must be moved (when structure offset will be reset below
If (!_uix_||_struct_[_uix_])
_offset_+=_IsPtr_?A_PtrSize:(_Struct.HasKey(_ArrType_)?_Struct[_ArrType_]:%_ArrType_%)*this[" " _ArrName_]
}
; Check for ENDING union and reset offset and union helpers
While (SubStr(_LF_BKP_,0)="}"){
If (!_uix_:=_union_.MaxIndex()){
MsgBox,0, Incorrect structure, missing opening braket {`nProgram will exit now `n%_TYPE_%
ExitApp
} ; Increase total size of union/structure if necessary
; reset offset and align because we left a union or structure
if (_uix_>1 && _struct_[_uix_-1]){
if (_mod_:=Mod(_offset_,_struct_align_[_uix_]))
_offset_+=Mod(_struct_align_[_uix_]-_mod_,_struct_align_[_uix_])
} else _offset_:=_union_[_uix_]
if (_struct_[_uix_]&&_struct_align_[_uix_]>_align_total_)
_align_total_ := _struct_align_[_uix_]
; Increase total size of union/structure if necessary
_total_union_size_ := _union_size_[_uix_]>_total_union_size_?_union_size_[_uix_]:_total_union_size_
,_union_._Remove(),_struct_._Remove(),_union_size_._Remove(),_struct_align_.Remove(),_LF_BKP_:=SubStr(_LF_BKP_,1,StrLen(_LF_BKP_)-1) ; remove latest items
If (_uix_=1){ ; leaving top union, add offset
if (_mod_:=Mod(_total_union_size_,_align_total_))
_total_union_size_ += Mod(_align_total_-_mod_,_align_total_)
_offset_+=_total_union_size_,_total_union_size_:=0
}
}
}
this.base:=_base_ ; apply new base which uses below functions and uses ___GET for __GET and ___SET for __SET
If (IsObject(_init_)||IsObject(_pointer_)){ ; Initialization of structures members, e.g. _Struct(_RECT,{left:10,right:20})
for _key_,_value_ in IsObject(_init_)?_init_:_pointer_
{
If !this["`r" _key_]{ ; It is not a pointer, assign value
If InStr(",LPSTR,LPCSTR,LPTSTR,LPCTSTR,LPWSTR,LPCWSTR,","," this["`t" _key_] ",")
this.Alloc(_key_,StrLen(_value_)*(InStr(".LPWSTR,LPCWSTR,","," this["`t"] ",")||(InStr(",LPTSTR,LPCTSTR,","," this["`t" _key_] ",")&&A_IsUnicode)?2:1))
if InStr(",LPSTR,LPCSTR,LPTSTR,LPCTSTR,LPWSTR,LPCWSTR,CHAR,TCHAR,WCHAR,","," this["`t" _key_] ",")
this[_key_]:=_value_
else
this[_key_] := _value_
}else if (_value_<>"") ; It is not empty
if _value_ is integer ; It is a new pointer
this[_key_][""]:=_value_
}
}
Return this
}
ToObj(struct:=""){
obj:=[]
for k,v in struct?struct:struct:=this
if (Asc(k)=10)
If IsObject(_VALUE_:=struct[_TYPE_:=SubStr(k,2)])
obj[_TYPE_]:=this.ToObj(_VALUE_)
else obj[_TYPE_]:=_VALUE_
return obj
}
SizeT(_key_=""){
return sizeof(this["`t" _key_])
}
Size(){
return sizeof(this)
}
IsPointer(_key_=""){
return this["`r" _key_]
}
Type(_key_=""){
return this["`t" _key_]
}
AHKType(_key_=""){
return this["`n" _key_]
}
Offset(_key_=""){
return this["`b" _key_]
}
Encoding(_key_=""){
return this["`b" _key_]
}
Capacity(_key_=""){
return this._GetCapacity("`v" _key_)
}
Alloc(_key_="",size="",ptrsize=0){
If _key_ is integer
ptrsize:=size,size:=_key_,_key_:=""
If size is integer
SizeIsInt:=1
If ptrsize {
If (this._SetCapacity("`v" _key_,!SizeIsInt?A_PtrSize+ptrsize:size + (size//A_PtrSize)*ptrsize)="")
MsgBox % "Memory for pointer ." _key_ ". of size " (SizeIsInt?size:A_PtrSize) " could not be set!"
else {
DllCall("RtlZeroMemory","UPTR",this._GetAddress("`v" _key_),"UInt",this._GetCapacity("`v" _key_))
If (this[" " _key_]>1){
ptr:=this[""] + this["`b" _key_]
If (this["`r" _key_] || InStr(",LPSTR,LPCSTR,LPTSTR,LPCTSTR,LPWSTR,LPCWSTR,","," this["`t" _key_] ","))
NumPut(ptrs:=this._GetAddress("`v" _key_),ptr+0,"PTR")
else if _key_
this[_key_,""]:=ptrs:=this._GetAddress("`v" _key_)
else this[""]:=ptr:=this._GetAddress("`v" _key_),ptrs:=this._GetAddress("`v" _key_)+(SizeIsInt?size:A_PtrSize)
} else {
If (this["`r" _key_] || InStr(",LPSTR,LPCSTR,LPTSTR,LPCTSTR,LPWSTR,LPCWSTR,","," this["`t" _key_] ","))
NumPut(ptr:=this._GetAddress("`v" _key_),this[""] + this["`b" _key_],"PTR")
else this[""]:=ptr:=this._GetAddress("`v" _key_)
ptrs:=ptr+(size?size:A_PtrSize)
}
Loop % SizeIsInt?(size//A_PtrSize):1
NumPut(ptrs+(A_Index-1)*ptrsize,ptr+(A_Index-1)*A_PtrSize,"PTR")
}
} else {
If (this._SetCapacity("`v" _key_,SizeIsInt?size:A_PtrSize)=""){
MsgBox % "Memory for pointer ." _key_ ". of size " (SizeIsInt?size:A_PtrSize) " could not be set!"
} else
NumPut(ptr:=this._GetAddress("`v" _key_),this[""] + this["`b" _key_],"PTR"),DllCall("RtlZeroMemory","UPTR",ptr,"UInt",SizeIsInt?size:A_PtrSize)
}
return ptr
}
___NEW(init*){
this:=this.base
newobj := this.__Clone(1) ;clone structure and keep pointer (1), it will be changed below
If (init.MaxIndex() && !IsObject(init.1))
newobj[""] := init.1
else If (init.MaxIndex()>1 && !IsObject(init.2))
newobj[""] := init.2
else
newobj._SetCapacity("`a",_StructSize_:=sizeof(this)) ; Set Capacity in key ["`a"]
,newobj[""]:=newobj._GetAddress("`a") ; Save pointer in key [""]
,DllCall("RtlZeroMemory","UPTR",newobj[""],"UInt",_StructSize_) ; zero-fill memory
If (IsObject(init.1)||IsObject(init.2))
for _key_,_value_ in IsObject(init.1)?init.1:init.2
newobj[_key_] := _value_
return newobj
}
; Clone structure and move pointer for new structure
___Clone(offset){
static _base_:={__GET:_Struct.___GET,__SET:_Struct.___SET,__SETPTR:_Struct.___SETPTR,__Clone:_Struct.___Clone,__NEW:_Struct.___NEW
,IsPointer:_Struct.IsPointer,Offset:_Struct.Offset,Type:_Struct.Type,AHKType:_Struct.AHKType,Encoding:_Struct.Encoding
,Capacity:_Struct.Capacity,Alloc:_Struct.Alloc,Size:_Struct.Size,SizeT:_Struct.SizeT,Print:_Struct.Print,ToObj:_Struct.ToObj}
If offset=1
return this
newobj:={} ; new structure object
for _key_,_value_ in this ; copy all values/objects
If (_key_!="`a")
newobj[_key_]:=_value_ ; add key to new object and assign value
newobj._SetCapacity("`a",_StructSize_:=sizeof(this)) ; Set Capacity in key ["`a"]
,newobj[""]:=newobj._GetAddress("`a") ; Save pointer in key [""]
,DllCall("RtlZeroMemory","UPTR",newobj[""],"UInt",_StructSize_) ; zero-fill memory
If this["`r"]{ ; its a pointer so we need too move internal memory
NumPut(NumGet(this[""],"PTR")+A_PtrSize*(offset-1),newobj[""],"Ptr")
newobj.base:=_base_ ;assign base of _Struct
} else ; do not use internal memory, simply assign new pointer to new structure
newobj.base:=_base_,newobj[]:=this[""]+sizeof(this)*(offset-1)
return newobj ; return new object
}
___GET(_key_="",p*){
If (_key_="") ; Key was not given so structure[] has been called, return pointer to structure
Return this[""]
else if !(idx:=p.MaxIndex())
_field_:=_key_,opt:="~"
else {
ObjInsert(p,1,_key_)
opt:=ObjRemove(p),_field_:=_key_:=ObjRemove(p)
for key_,value_ in p
this:=this[value_]
}
If this["`t"] ; structure without keys/members
_key_:="" ; set _key_ empty so items below will resolve to our structure
If (opt!="~"){
If (opt=""){
If _field_ is integer
return (this["`r"]?NumGet(this[""],"PTR"):this[""])+sizeof(this["`t"])*(_field_-1)
else return this["`r" _key_]?NumGet(this[""]+this["`b" _key_],"PTR"):this[""]+this["`b" _key_] ;+sizeof(this["`t" _key_])*(_field_-1)
} else If opt is integer
{ ;offset to a item e.g. struct.a[100] ("Uint a[100]")
; MsgBox % "ja "
If (_Struct.HasKey("_" this["`t" _key_]) && this[" " _key_]>1) {
If (InStr( ",CHAR,UCHAR,TCHAR,WCHAR," , "," this["`t" _key_] "," )){ ; StrGet 1 character only
Return StrGet(this[""]+this["`b" _key_]+(opt-1)*sizeof(this["`t" _key_]),1,this["`f" _key_])
} else if InStr( ",LPSTR,LPCSTR,LPTSTR,LPCTSTR,LPWSTR,LPCWSTR," , "," this["`t" _key_] "," ){ ; StrGet string
Return StrGet(NumGet(this[""]+this["`b" _key_]+(opt-1)*A_PtrSize,"PTR"),this["`f" _key_])
} else {
Return NumGet(this[""]+this["`b" _key_]+(opt-1)*sizeof(this["`t" _key_]),this["`n" _key_])
}
} else Return new _Struct(this["`t" _key_],this[""]+this["`b" _key_]+(opt-1)*sizeof(this["`t" _key_]))
} else
return this[_key_][opt]
} else If _field_ is integer
{ ; array access (must be listed first because otherwise this["`r" _key_] cannot be resolved
If (_key_){ ; Offset for item
return this.__Clone(_field_)
} else if this["`r"] {
Pointer:=""
Loop % (this["`r"]-1) ; dip into one step and return a new structure
pointer.="*"
If pointer
Return new _Struct(pointer this["`t"],NumGet(this[""],"PTR")+A_PtrSize*(_field_-1))
else Return new _Struct(pointer this["`t"],NumGet(this[""],"PTR")+sizeof(this["`t"])*(_field_-1)).1
} else if _Struct.HasKey("_" this["`t"]) {
; If this[" "]
; Return new _Struct(this["`t"],this[""])
; else
If (InStr( ",CHAR,UCHAR,TCHAR,WCHAR," , "," this["`t"] "," )){ ; StrGet 1 character only
Return StrGet(this[""]+sizeof(this["`t"])*(_field_-1),1,this["`f"])
} else if InStr(",LPSTR,LPCSTR,LPTSTR,LPCTSTR,LPWSTR,LPCWSTR," , "," this["`t"] "," ){ ; StrGet string
Return StrGet(NumGet(this[""]+A_PtrSize*(_field_-1),"PTR"),this["`f"])
} else { ; resolve pointer
Return NumGet(this[""]+sizeof(this["`t"])*(_field_-1),this["`n"])
}
} else {
; return this.__Clone(_field_)
; listVars
; MsgBox % this[""] "+" sizeof(this["`t"])*(_field_-1) "-" this["`t"]
Return new _Struct(this["`t"],this[""]+sizeof(this["`t"])*(_field_-1))
}
} else If this["`r" _key_] { ;pointer
Pointer:=""
Loop % (this["`r" _key_]-1) ; dip into one step and return a new structure
pointer.="*"
If (_key_=""){
return this[1][_field_]
} else {
Return new _Struct(pointer this["`t" _key_],NumGet(this[""]+this["`b" _key_],"PTR"))
}
} else if _Struct.HasKey("_" this["`t" _key_]) { ; default data type, not pointer
If (this[" " _key_]>1)
Return new _Struct(this["`t" _key_],this[""] + this["`b" _key_])
else If (InStr( ",CHAR,UCHAR,TCHAR,WCHAR," , "," this["`t" _key_] "," )){ ; StrGet 1 character only
Return StrGet(this[""]+this["`b" _key_],1,this["`f" _key_])
} else if InStr( ",LPSTR,LPCSTR,LPTSTR,LPCTSTR,LPWSTR,LPCWSTR," , "," this["`t" _key_] "," ){ ; StrGet string
Return StrGet(NumGet(this[""]+this["`b" _key_],"PTR"),this["`f" _key_])
} else {
Return NumGet(this[""]+this["`b" _key_],this["`n" _key_])
}
} else { ; the field is a non pointer structure
Return new _Struct(this["`t" _key_],this[""]+this["`b" _key_])
}
}
___SET(_key_,p*){ ;="",_value_=-0x8000000000000000 ,opt="~"){
If !(idx:=p.MaxIndex()) ; Set new Pointer, here a value was assigned e.g. struct[]:=&var
return this[""] :=_key_,this._SetCapacity("`a",0) ; free internal memory, it will not be used anymore
else if (idx=1)
_value_:=p.1,opt:="~"
else if (idx>1){
ObjInsert(p,1,_key_)
If (p[idx]="")
opt:=ObjRemove(p),_value_:=ObjRemove(p),_key_:=ObjRemove(p)
else _value_:=ObjRemove(p),_key_:=ObjRemove(p),opt:="~"
for key_,value_ in p
this:=this[value_]
}
If this["`t"] ; structure without members
_field_:=_key_,_key_:="" ; set _key_ empty so it will resolve to our structure
else _field_:=_key_
If this["`r" _key_] { ; Pointer
If opt is integer
return NumPut(opt,this[""] + this["`b" _key_],"PTR")
else if this.HasKey("`t" _key_) {
Pointer:=""
Loop % (this["`r" _key_]-1) ; dip into one step and return a new structure
pointer.="*"
If _key_
Return (new _Struct(pointer this["`t" _key_],NumGet(this[""] + this["`b" _key_],"PTR"))).1:=_value_
else Return (new _Struct(pointer this["`t"],NumGet(this[""],"PTR")))[_field_]:=_value_
} else If _field_ is Integer
if (_key_="") ; replace this for the operation
_this:=this,this:=this.__Clone(_Field_)
If InStr( ",LPSTR,LPCSTR,LPTSTR,LPCTSTR,LPWSTR,LPCWSTR," , "," this["`t" _key_] "," )
StrPut(_value_,NumGet(NumGet(this[""]+this["`b" _key_],"PTR"),"PTR"),this["`f" _key_]) ; StrPut char to addr+A_PtrSize
else if InStr( ",TCHAR,CHAR,UCHAR,WCHAR," , "," this["`t" _key_] "," ){ ; same as above but for 1 Character only
StrPut(_value_,NumGet(this[""]+this["`b" _key_],"PTR"),this["`f" _key_]) ; StrPut char to addr
} else
NumPut(_value_,NumGet(this[""]+this["`b" _key_],"PTR"),this["`n" _key_])
If _field_ is integer ; restore this after operation
this:=_this
} else if (RegExMatch(_field_,"^\d+$") && _key_="") {
if InStr( ",LPSTR,LPCSTR,LPTSTR,LPCTSTR,LPWSTR,LPCWSTR," , "," this["`t"] "," ){
StrPut(_value_,NumGet(this[""]+A_PtrSize*(_field_-1),"PTR"),this["`f"]) ; StrPut string to addr
} else if InStr( ",TCHAR,CHAR,UCHAR,WCHAR," , "," this["`t" _key_] "," ){
StrPut(_value_,this[""] + sizeof(this["`t"])*(_field_-1),this["`f"])
} else
NumPut(_value_,this[""] + sizeof(this["`t"])*(_field_-1),this["`n"]) ; NumPut new value to key
} else if opt is integer
{
return NumPut(opt,this[""] + this["`b" _key_],"PTR")
} else if InStr( ",LPSTR,LPCSTR,LPTSTR,LPCTSTR,LPWSTR,LPCWSTR," , "," this["`t" _key_] "," ){
StrPut(_value_,NumGet(this[""] + this["`b" _key_],"PTR"),this["`f" _key_]) ; StrPut string to addr
} else if InStr( ",TCHAR,CHAR,UCHAR,WCHAR," , "," this["`t" _key_] "," ){
StrPut(_value_,this[""] + this["`b" _key_],this["`f" _key_]) ; StrPut character key
} else
NumPut(_value_,this[""]+this["`b" _key_],this["`n" _key_]) ; NumPut new value to key
Return _value_
}
}
sizeof(_TYPE_,parent_offset=0,ByRef _align_total_=0){
;Windows and AHK Data Types, used to find out the corresponding size
static _types__:="
(LTrim Join
,ATOM:2,LANGID:2,WCHAR:2,WORD:2,PTR:" A_PtrSize ",UPTR:" A_PtrSize ",SHORT:2,USHORT:2,INT:4,UINT:4,INT64:8,UINT64:8,DOUBLE:8,FLOAT:4,CHAR:1,UCHAR:1,__int64:8
,TBYTE:" (A_IsUnicode?2:1) ",TCHAR:" (A_IsUnicode?2:1) ",HALF_PTR:" (A_PtrSize=8?4:2) ",UHALF_PTR:" (A_PtrSize=8?4:2) ",INT32:4,LONG:4,LONG32:4,LONGLONG:8
,LONG64:8,USN:8,HFILE:4,HRESULT:4,INT_PTR:" A_PtrSize ",LONG_PTR:" A_PtrSize ",POINTER_64:" A_PtrSize ",POINTER_SIGNED:" A_PtrSize "
,BOOL:4,SSIZE_T:" A_PtrSize ",WPARAM:" A_PtrSize ",BOOLEAN:1,BYTE:1,COLORREF:4,DWORD:4,DWORD32:4,LCID:4,LCTYPE:4,LGRPID:4,LRESULT:4,PBOOL:" A_PtrSize "
,PBOOLEAN:" A_PtrSize ",PBYTE:" A_PtrSize ",PCHAR:" A_PtrSize ",PCSTR:" A_PtrSize ",PCTSTR:" A_PtrSize ",PCWSTR:" A_PtrSize ",PDWORD:" A_PtrSize "
,PDWORDLONG:" A_PtrSize ",PDWORD_PTR:" A_PtrSize ",PDWORD32:" A_PtrSize ",PDWORD64:" A_PtrSize ",PFLOAT:" A_PtrSize ",PHALF_PTR:" A_PtrSize "
,UINT32:4,ULONG:4,ULONG32:4,DWORDLONG:8,DWORD64:8,ULONGLONG:8,ULONG64:8,DWORD_PTR:" A_PtrSize ",HACCEL:" A_PtrSize ",HANDLE:" A_PtrSize "
,HBITMAP:" A_PtrSize ",HBRUSH:" A_PtrSize ",HCOLORSPACE:" A_PtrSize ",HCONV:" A_PtrSize ",HCONVLIST:" A_PtrSize ",HCURSOR:" A_PtrSize ",HDC:" A_PtrSize "
,HDDEDATA:" A_PtrSize ",HDESK:" A_PtrSize ",HDROP:" A_PtrSize ",HDWP:" A_PtrSize ",HENHMETAFILE:" A_PtrSize ",HFONT:" A_PtrSize ",USAGE:" 2 "
)"
static _types_:=_types__ "
(LTrim Join
,HGDIOBJ:" A_PtrSize ",HGLOBAL:" A_PtrSize ",HHOOK:" A_PtrSize ",HICON:" A_PtrSize ",HINSTANCE:" A_PtrSize ",HKEY:" A_PtrSize ",HKL:" A_PtrSize "
,HLOCAL:" A_PtrSize ",HMENU:" A_PtrSize ",HMETAFILE:" A_PtrSize ",HMODULE:" A_PtrSize ",HMONITOR:" A_PtrSize ",HPALETTE:" A_PtrSize ",HPEN:" A_PtrSize "
,HRGN:" A_PtrSize ",HRSRC:" A_PtrSize ",HSZ:" A_PtrSize ",HWINSTA:" A_PtrSize ",HWND:" A_PtrSize ",LPARAM:" A_PtrSize ",LPBOOL:" A_PtrSize ",LPBYTE:" A_PtrSize "
,LPCOLORREF:" A_PtrSize ",LPCSTR:" A_PtrSize ",LPCTSTR:" A_PtrSize ",LPCVOID:" A_PtrSize ",LPCWSTR:" A_PtrSize ",LPDWORD:" A_PtrSize ",LPHANDLE:" A_PtrSize "
,LPINT:" A_PtrSize ",LPLONG:" A_PtrSize ",LPSTR:" A_PtrSize ",LPTSTR:" A_PtrSize ",LPVOID:" A_PtrSize ",LPWORD:" A_PtrSize ",LPWSTR:" A_PtrSize "
,PHANDLE:" A_PtrSize ",PHKEY:" A_PtrSize ",PINT:" A_PtrSize ",PINT_PTR:" A_PtrSize ",PINT32:" A_PtrSize ",PINT64:" A_PtrSize ",PLCID:" A_PtrSize "
,PLONG:" A_PtrSize ",PLONGLONG:" A_PtrSize ",PLONG_PTR:" A_PtrSize ",PLONG32:" A_PtrSize ",PLONG64:" A_PtrSize ",POINTER_32:" A_PtrSize "
,POINTER_UNSIGNED:" A_PtrSize ",PSHORT:" A_PtrSize ",PSIZE_T:" A_PtrSize ",PSSIZE_T:" A_PtrSize ",PSTR:" A_PtrSize ",PTBYTE:" A_PtrSize "
,PTCHAR:" A_PtrSize ",PTSTR:" A_PtrSize ",PUCHAR:" A_PtrSize ",PUHALF_PTR:" A_PtrSize ",PUINT:" A_PtrSize ",PUINT_PTR:" A_PtrSize "
,PUINT32:" A_PtrSize ",PUINT64:" A_PtrSize ",PULONG:" A_PtrSize ",PULONGLONG:" A_PtrSize ",PULONG_PTR:" A_PtrSize ",PULONG32:" A_PtrSize "
,PULONG64:" A_PtrSize ",PUSHORT:" A_PtrSize ",PVOID:" A_PtrSize ",PWCHAR:" A_PtrSize ",PWORD:" A_PtrSize ",PWSTR:" A_PtrSize ",SC_HANDLE:" A_PtrSize "
,SC_LOCK:" A_PtrSize ",SERVICE_STATUS_HANDLE:" A_PtrSize ",SIZE_T:" A_PtrSize ",UINT_PTR:" A_PtrSize ",ULONG_PTR:" A_PtrSize ",VOID:" A_PtrSize "
)"
local _:="",_ArrName_:="",_ArrType_:="",_ArrSize_:=0,_defobj_:="",_idx_:=0,_LF_:="",_LF_BKP_:="",_match_:="",_offset_:=0,_padding_:=0,_struct_:=""
,_total_union_size_:=0,_uix_:=0,_union_:=0,_union_size_:=0,_in_struct_:=0,_mod_:=0,_max_size_:=0,_struct_align_:=0
_offset_:=parent_offset ; Init size/offset to 0 or parent_offset
If IsObject(_TYPE_){ ; If structure object - check for offset in structure and return pointer + last offset + its data size
return _TYPE_["`a`a"]
}
If RegExMatch(_TYPE_,"^[\w\d\._]+$"){ ; structures name was supplied, resolve to global var and run again
If InStr(_types_,"," _TYPE_ ":")
Return SubStr(_types_,InStr(_types_,"," _TYPE_ ":") + 2 + StrLen(_TYPE_),1)
else If InStr(_TYPE_,"."){ ;check for object that holds structure definition
Loop,Parse,_TYPE_,.
If A_Index=1
_defobj_:=%A_LoopField%
else _defobj_:=_defobj_[A_LoopField]
Return sizeof(_defobj_,parent_offset)
} else Return sizeof(%_TYPE_%,parent_offset)
} else _defobj_:=""
If InStr(_TYPE_,"`n") { ; C/C++ style definition, convert
_offset_:="" ; This will hold new structure
,_struct_:=[] ; This will keep track if union is structure
,_union_:=0 ; This will keep track of union depth
Loop,Parse,_TYPE_,`n,`r`t%A_Space%%A_Tab%
{
_LF_:=""
Loop,Parse,A_LoopField,`,`;,`t%A_Space%%A_Tab%
{
If RegExMatch(A_LoopField,"^\s*//") ;break on comments and continue main loop
break
If (A_LoopField){ ; skip empty lines
If (!_LF_ && _ArrType_:=RegExMatch(A_LoopField,"[\w\d_#@]\s+[\w\d_#@]")) ; new line, find out data type and save key in _LF_ Data type will be added later
_LF_:=RegExReplace(A_LoopField,"[\w\d_#@]\K\s+.*$")
If Instr(A_LoopField,"{"){ ; Union, also check if it is a structure
_union_++,_struct_.Insert(_union_,RegExMatch(A_LoopField,"i)^\s*struct\s*\{"))
} else If InStr(A_LoopField,"}") ; end of union/struct
_offset_.="}"
else { ; not starting or ending struct or union so add definitions and apply Data Type.
If _union_ ; add { or struct{
Loop % _union_
_ArrName_.=(_struct_[A_Index]?"struct":"") "{"
_offset_.=(_offset_ ? "," : "") _ArrName_ ((_ArrType_ && A_Index!=1)?(_LF_ " "):"") RegExReplace(A_LoopField,"\s+"," ")
,_ArrName_:="",_union_:=0
}
}
}
}
_TYPE_:=_offset_
,_offset_:=parent_offset ; Init size/offset to 0 or parent_offset
}
; Following keep track of union size/offset
_union_:=[] ; keep track of union level, required to reset offset after union is parsed
,_struct_:=[] ; for each union level keep track if it is a structure (because here offset needs to increase
,_union_size_:=[] ; keep track of highest member within the union or structure, used to calculate new offset after union
,_struct_align_:=[] ; keep track of alignment before structure
,_total_union_size_:=0 ; used in combination with above, each loop the total offset is updated if current data size is higher
;,_align_total_:=0 ; used to calculate alignment for total size of structure
,_in_struct_:=1
; Parse given structure definition and calculate size
; Structures will be resolved by recrusive calls (a structure must be global)
Loop,Parse,_TYPE_,`,`; ;,%A_Space%%A_Tab%`n`r
{
_in_struct_+=StrLen(A_LoopField)+1
If ("" = _LF_ := trim(A_LoopField,A_Space A_Tab "`n`r"))
continue
_LF_BKP_:=_LF_ ;to check for ending brackets = union,struct
; Check for STARTING union and set union helpers
While (_match_:=RegExMatch(_LF_,"i)^(struct|union)?\s*\{\K"))
; correct offset for union/structure, sizeof_maxsize returns max size of union or structure
_max_size_:=sizeof_maxsize(SubStr(_TYPE_,_in_struct_-StrLen(A_LoopField)-1+(StrLen(_LF_BKP_)-StrLen(_LF_))))
,_union_.Insert(_offset_+=(_mod_:=Mod(_offset_,_max_size_))?Mod(_max_size_-_mod_,_max_size_):0)
,_union_size_.Insert(0)
,_struct_align_.Insert(_align_total_>_max_size_?_align_total_:_max_size_)
,_struct_.Insert(RegExMatch(_LF_,"i)^struct\s*\{")?(1,_align_total_:=0):0)
,_LF_:=SubStr(_LF_,_match_)
StringReplace,_LF_,_LF_,},,A
If InStr(_LF_,"*"){ ; It's a pointer, size will be always A_PtrSize
_offset_ += (_mod_:=Mod(_offset_ + A_PtrSize,A_PtrSize)?A_PtrSize-_mod_:0) + A_PtrSize
,_align_total_:=_align_total_<A_PtrSize?A_PtrSize:_align_total_
} else {
; Split array type and optionally the size of array, e.g. "TCHAR chr[5]"
RegExMatch(_LF_,"^(?<ArrType_>[\w\d\._#@]+)?\s*(?<ArrName_>[\w\d\._#@]+)?\s*\[?(?<ArrSize_>\d+)?\]?\s*$",_)
If (!_ArrName_ && !_ArrSize_ && !InStr( _types_ ,"," _ArrType_ ":"))
_ArrName_:=_ArrType_,_ArrType_:="UInt"
If InStr(_ArrType_,"."){ ;check for object that holds structure definition
Loop,Parse,_ArrType_,.
If A_Index=1
_defobj_:=%A_LoopField%
else _defobj_:=_defobj_[A_LoopField]
; _ArrType_:=_defobj_ ; ??????????????????????????????????????
}
If (_idx_:=InStr( _types_ ,"," _ArrType_ ":")) ; AHK or Windows data type
_padding_:=SubStr( _types_ , _idx_+StrLen(_ArrType_)+2 , 1 ),_align_total_:=_align_total_<_padding_?_padding_:_align_total_
else _padding_:= sizeof(_defobj_?_defobj_:%_ArrType_%,0,_align_total_),_max_size_:=sizeof_maxsize(_defobj_?_defobj_:%_ArrType_%)
if (_max_size_){
if (_mod_:=Mod(_offset_,_max_size_))
_offset_ += Mod(_max_size_-_mod_,_max_size_)
} else if _mod_:=Mod(_offset_,_padding_)
_offset_ += Mod(_padding_-_mod_,_padding_)
_offset_ += (_padding_ * (_ArrSize_?_ArrSize_:1))
_max_size_:=0
}
; It's a union or struct, check if new member is higher then previous members
If (_uix_:=_union_.MaxIndex()) && (_max_size_:=_offset_ - _union_[_uix_])>_union_size_[_uix_]
_union_size_[_uix_]:=_max_size_
_max_size_:=0
; It's a union and not struct
If (_uix_ && !_struct_[_uix_])
_offset_:=_union_[_uix_]
; Check for ENDING union and reset offset and union helpers
While (SubStr(_LF_BKP_,0)="}"){
If !(_uix_:=_union_.MaxIndex()){
MsgBox,0, Incorrect structure, missing opening braket {`nProgram will exit now `n%_TYPE_%
ExitApp
}
; reset offset and align because we left a union or structure
if (_uix_>1 && _struct_[_uix_-1]){
If (_mod_:=Mod(_offset_,_struct_align_[_uix_]))
_offset_+=Mod(_struct_align_[_uix_]-_mod_,_struct_align_[_uix_])
} else _offset_:=_union_[_uix_]
; a member of union/struct is smaller than previous align, restore
if (_struct_[_uix_] &&_struct_align_[_uix_]>_align_total_)
_align_total_ := _struct_align_[_uix_]
; Increase total size of union/structure if necessary
_total_union_size_ := _union_size_[_uix_]>_total_union_size_?_union_size_[_uix_]:_total_union_size_
,_union_.Remove() ,_struct_.Remove() ,_union_size_.Remove(),_struct_align_.Remove() ; remove latest items
,_LF_BKP_:=SubStr(_LF_BKP_,1,StrLen(_LF_BKP_)-1)
If (_uix_=1){ ; leaving top union, add offset
if (_mod_:=Mod(_total_union_size_,_align_total_))
_total_union_size_ += Mod(_align_total_-_mod_,_align_total_)
_offset_+=_total_union_size_,_total_union_size_:=0
}
}
}
_offset_+= Mod(_align_total_ - Mod(_offset_,_align_total_),_align_total_)
Return _offset_
}
sizeof_maxsize(s){
static _types__:="
(LTrim Join
,ATOM:2,LANGID:2,WCHAR:2,WORD:2,PTR:" A_PtrSize ",UPTR:" A_PtrSize ",SHORT:2,USHORT:2,INT:4,UINT:4,INT64:8,UINT64:8,DOUBLE:8,FLOAT:4,CHAR:1,UCHAR:1,__int64:8
,TBYTE:" (A_IsUnicode?2:1) ",TCHAR:" (A_IsUnicode?2:1) ",HALF_PTR:" (A_PtrSize=8?4:2) ",UHALF_PTR:" (A_PtrSize=8?4:2) ",INT32:4,LONG:4,LONG32:4,LONGLONG:8
,LONG64:8,USN:8,HFILE:4,HRESULT:4,INT_PTR:" A_PtrSize ",LONG_PTR:" A_PtrSize ",POINTER_64:" A_PtrSize ",POINTER_SIGNED:" A_PtrSize "
,BOOL:4,SSIZE_T:" A_PtrSize ",WPARAM:" A_PtrSize ",BOOLEAN:1,BYTE:1,COLORREF:4,DWORD:4,DWORD32:4,LCID:4,LCTYPE:4,LGRPID:4,LRESULT:4,PBOOL:" A_PtrSize "
,PBOOLEAN:" A_PtrSize ",PBYTE:" A_PtrSize ",PCHAR:" A_PtrSize ",PCSTR:" A_PtrSize ",PCTSTR:" A_PtrSize ",PCWSTR:" A_PtrSize ",PDWORD:" A_PtrSize "
,PDWORDLONG:" A_PtrSize ",PDWORD_PTR:" A_PtrSize ",PDWORD32:" A_PtrSize ",PDWORD64:" A_PtrSize ",PFLOAT:" A_PtrSize ",PHALF_PTR:" A_PtrSize "
,UINT32:4,ULONG:4,ULONG32:4,DWORDLONG:8,DWORD64:8,ULONGLONG:8,ULONG64:8,DWORD_PTR:" A_PtrSize ",HACCEL:" A_PtrSize ",HANDLE:" A_PtrSize "
,HBITMAP:" A_PtrSize ",HBRUSH:" A_PtrSize ",HCOLORSPACE:" A_PtrSize ",HCONV:" A_PtrSize ",HCONVLIST:" A_PtrSize ",HCURSOR:" A_PtrSize ",HDC:" A_PtrSize "
,HDDEDATA:" A_PtrSize ",HDESK:" A_PtrSize ",HDROP:" A_PtrSize ",HDWP:" A_PtrSize ",HENHMETAFILE:" A_PtrSize ",HFONT:" A_PtrSize ",USAGE:" 2 "
)"
static _types_:=_types__ "
(LTrim Join
,HGDIOBJ:" A_PtrSize ",HGLOBAL:" A_PtrSize ",HHOOK:" A_PtrSize ",HICON:" A_PtrSize ",HINSTANCE:" A_PtrSize ",HKEY:" A_PtrSize ",HKL:" A_PtrSize "
,HLOCAL:" A_PtrSize ",HMENU:" A_PtrSize ",HMETAFILE:" A_PtrSize ",HMODULE:" A_PtrSize ",HMONITOR:" A_PtrSize ",HPALETTE:" A_PtrSize ",HPEN:" A_PtrSize "
,HRGN:" A_PtrSize ",HRSRC:" A_PtrSize ",HSZ:" A_PtrSize ",HWINSTA:" A_PtrSize ",HWND:" A_PtrSize ",LPARAM:" A_PtrSize ",LPBOOL:" A_PtrSize ",LPBYTE:" A_PtrSize "
,LPCOLORREF:" A_PtrSize ",LPCSTR:" A_PtrSize ",LPCTSTR:" A_PtrSize ",LPCVOID:" A_PtrSize ",LPCWSTR:" A_PtrSize ",LPDWORD:" A_PtrSize ",LPHANDLE:" A_PtrSize "
,LPINT:" A_PtrSize ",LPLONG:" A_PtrSize ",LPSTR:" A_PtrSize ",LPTSTR:" A_PtrSize ",LPVOID:" A_PtrSize ",LPWORD:" A_PtrSize ",LPWSTR:" A_PtrSize "
,PHANDLE:" A_PtrSize ",PHKEY:" A_PtrSize ",PINT:" A_PtrSize ",PINT_PTR:" A_PtrSize ",PINT32:" A_PtrSize ",PINT64:" A_PtrSize ",PLCID:" A_PtrSize "
,PLONG:" A_PtrSize ",PLONGLONG:" A_PtrSize ",PLONG_PTR:" A_PtrSize ",PLONG32:" A_PtrSize ",PLONG64:" A_PtrSize ",POINTER_32:" A_PtrSize "
,POINTER_UNSIGNED:" A_PtrSize ",PSHORT:" A_PtrSize ",PSIZE_T:" A_PtrSize ",PSSIZE_T:" A_PtrSize ",PSTR:" A_PtrSize ",PTBYTE:" A_PtrSize "
,PTCHAR:" A_PtrSize ",PTSTR:" A_PtrSize ",PUCHAR:" A_PtrSize ",PUHALF_PTR:" A_PtrSize ",PUINT:" A_PtrSize ",PUINT_PTR:" A_PtrSize "
,PUINT32:" A_PtrSize ",PUINT64:" A_PtrSize ",PULONG:" A_PtrSize ",PULONGLONG:" A_PtrSize ",PULONG_PTR:" A_PtrSize ",PULONG32:" A_PtrSize "
,PULONG64:" A_PtrSize ",PUSHORT:" A_PtrSize ",PVOID:" A_PtrSize ",PWCHAR:" A_PtrSize ",PWORD:" A_PtrSize ",PWSTR:" A_PtrSize ",SC_HANDLE:" A_PtrSize "
,SC_LOCK:" A_PtrSize ",SERVICE_STATUS_HANDLE:" A_PtrSize ",SIZE_T:" A_PtrSize ",UINT_PTR:" A_PtrSize ",ULONG_PTR:" A_PtrSize ",VOID:" A_PtrSize "
)"
max:=0,i:=0
s:=trim(s,"`n`r`t ")
If InStr(s,"}"){
Loop,Parse,s
if (A_LoopField="{")
i++
else if (A_LoopField="}"){
if --i<1{
end:=A_Index
break
}
}
if end
s:=SubStr(s,1,end)
}
Loop,Parse,s,`n,`r
{
_struct_:=(i:=InStr(A_LoopField," //"))?SubStr(A_LoopField,1,i):A_LoopField
Loop,Parse,_struct_,`;`,{},%A_Space%%A_Tab%
if A_LoopField&&!InStr(".union.struct.","." A_LoopField ".")
if (!InStr(A_LoopField,A_Tab)&&!InStr(A_LoopField," "))
max:=max<4?4:max
else if (sizeof(A_LoopField,0,size:=0) && max<size)
max:=size
}
return max
}
Code: Select all
YamlText=
(
menu:
page_size: 5
punctuator:
full_shape:
" ": {commit: " "}
"!": {commit: "!"}
"\"": {pair: ["“", "”"]}
"#": ["#", "⌘"]
"$": ["¥", "$", "€", "£", "¥", "¢", "¤"]
"%": ["%", "°", "℃"]
"&": "&"
"'": {pair: ["‘", "’"]}
"(": "("
")": ")"
"*": ["*", "·", "・", "×", "※", "❂"]
"+": "+"
",": {commit: ","}
"-": "-"
.: {commit: "。"}
"/": ["/", "÷"]
":": {commit: ":"}
";": {commit: ";"}
"<": ["《", "〈", "«", "‹"]
"=": "="
">": ["》", "〉", "»", "›"]
"?": {commit: "?"}
"@": ["@", "☯"]
"[": ["「", "【", "〔", "["]
"\\": ["、", "\"]
"]": ["」", "】", "〕", "]"]
"^": {commit: "……"}
_: "——"
"`": "`"
"{": ["『", "〖", "{"]
"|": ["·", "|", "§", "¦"]
"}": ["』", "〗", "}"]
"~": "~"
recognizer:
patterns:
email: "^[A-Za-z][-_.0-9A-Za-z]*@.*$"
)
y:=Yaml(YamlText,0)
MsgBox % y.menu.page_size
MsgBox % y.recognizer.patterns.email
return