Yaml.ahk read Rime.Yaml error

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
cgx5871
Posts: 315
Joined: 26 Jul 2018, 14:02

Yaml.ahk read Rime.Yaml error

Post by cgx5871 » 05 Oct 2022, 16:08

Rime.Yaml:

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]*@.*$"
Yaml.ahk:

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
}
Test.ahk:

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

cgx5871
Posts: 315
Joined: 26 Jul 2018, 14:02

Re: Yaml.ahk read Rime.Yaml error

Post by cgx5871 » 07 Oct 2022, 15:06

Code: Select all

Loop,Parse,BackupVars,`,
          __%A_LoopField%:=_%A_LoopField%
          ,__%A_LoopField%%_PRV%:=_%A_LoopField%
,__%A_LoopField%%_PRV%:=_%A_LoopField%
It seems that this line will never run. What is the use of this line?

Post Reply

Return to “Ask for Help (v1)”