ObjRawSet & Array V1 => V2

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
AHK_user
Posts: 515
Joined: 04 Dec 2015, 14:52
Location: Belgium

ObjRawSet & Array V1 => V2

03 Sep 2021, 10:43

Can somebody confirm that this conversion is correct?

Code: Select all

;V1
ObjRawSet(this, ":", {Mask: {O: 4, T: "UInt"}, Effects: {O: 8, T: "UInt"}, Height: {O: 12, T: "Int"}, Offset: {O: 16, T: "Int"}})

Code: Select all

;V2
This := Object()
This.DefineProp(":","Set") := Map("Mask", Map("O", 4, "T", "UInt"), "Effects", Map("O", 8, "T", "UInt"), "Height", Map("O", 12, "T", "Int"), "Offset", Map("O", 16, "T", "Int"))
Personally, I find the old way of defining arrays more readable
User avatar
TheArkive
Posts: 1027
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: ObjRawSet & Array V1 => V2

03 Sep 2021, 10:50

You don't need to use Map() for that. It just depends on how you intend to iterate through it, or how you intend to access the members.

I'm not sure what you are trying to do with it. Can you paste a v1 example of how the members are accessed or iterated through?

EDIT:

I'm a bit of a noob on this kind of stuff, and i know this isn't exactly what ya asked for, but I was able to get this to work:

Code: Select all

this := {} ; or Object()
this.DefineProp("a",{Value: {Mask:{O:4, T:"UInt"}, Effects:{O:8, T:"UInt"}} })
msgbox this.a.Mask.O " / " this.a.Mask.T ; returns "4 / UInt"

Are you trying to define several values at once? Trying to make them read only?

I saw you specified {Set:} which requires a function object, but you appear to be assigning constants just for referencing (or some such), so that would require {Value:}.
lexikos
Posts: 9560
Joined: 30 Sep 2013, 04:07
Contact:

Re: ObjRawSet & Array V1 => V2

03 Sep 2021, 18:36

Of course it's not correct. If you attempt to test it, you will see that there is a syntax error.

Also, that's not an array in the literal sense, or even an Array. ObjRawSet is not intended for arrays, and I would not consider its use with arrays to be readable. ":" as a key doesn't lend itself to readable code, either.

I would guess that your script works like this:
  • O and T correspond to the Offset and Type parameters of NumGet/NumPut.
  • Meta-functions are used to implement properties which retrieve numbers from a struct using NumGet, or set them with NumPut.
  • The object in ":" contains a key: (or property name) for each property implemented by the meta-functions.
  • ObjRawSet is needed to avoid invoking __Set.
How close am I?

Rather than using DefineProp to bypass the meta-functions, it would be better to replace the meta-functions with DefineProp. For example:

Code: Select all

class Point extends Buffer {
	static __new() {
		inst := this.prototype
		inst.DefineProp 'x', {
			get: NumGet.Bind( , 0, 'int'),
			set: (this, value) => NumPut('int', value, this, 0)
		}
		inst.DefineProp 'y', {
			get: NumGet.Bind( , 4, 'int'),
			set: inst._xset.Bind( , {O:4, T:'int'})
		}
	}
	_xset(p, value) {
		NumPut(p.T, value, this, p.O)
	}
	__new() => super.__new(8, 0)
}

DllCall('GetCursorPos', 'ptr', pt := Point())
MsgBox pt.x ',' pt.y
pt.x := 1, pt.y := 2
MsgBox pt.x ',' pt.y
  • The properties are defined once only, for all instances of Point.
  • I've shown a few different ways to define the getter and setter.
  • There are no meta-functions, so you can assign new properties directly, if needed.
  • Some debugger clients can discover the x and y properties and list them for you, because they have been explicitly defined. (For instance, DebugVars and VSCode with vscode-autohotkey-debug will show you the values of x and y if you expand the object and its base, since that's where the getter/setter is defined.)
Of course, if you are hard-coding each property like this, you can define the properties directly in the class rather than with DefineProp. Conversely, the property name, offset and type can come from an array or object. For example, you can enumerate the properties of your {Mask: {O: 4, T: "UInt"}, ... } object with ObjOwnProps; you do not need to change the syntax of the object literal at all.
AHK_user
Posts: 515
Joined: 04 Dec 2015, 14:52
Location: Belgium

Re: ObjRawSet & Array V1 => V2

04 Sep 2021, 04:44

lexikos wrote:
03 Sep 2021, 18:36
Of course it's not correct. If you attempt to test it, you will see that there is a syntax error.

Also, that's not an array in the literal sense, or even an Array. ObjRawSet is not intended for arrays, and I would not consider its use with arrays to be readable. ":" as a key doesn't lend itself to readable code, either.

I would guess that your script works like this:
  • O and T correspond to the Offset and Type parameters of NumGet/NumPut.
  • Meta-functions are used to implement properties which retrieve numbers from a struct using NumGet, or set them with NumPut.
  • The object in ":" contains a key: (or property name) for each property implemented by the meta-functions.
  • ObjRawSet is needed to avoid invoking __Set.
How close am I?

Rather than using DefineProp to bypass the meta-functions, it would be better to replace the meta-functions with DefineProp. For example:

Code: Select all

class Point extends Buffer {
	static __new() {
		inst := this.prototype
		inst.DefineProp 'x', {
			get: NumGet.Bind( , 0, 'int'),
			set: (this, value) => NumPut('int', value, this, 0)
		}
		inst.DefineProp 'y', {
			get: NumGet.Bind( , 4, 'int'),
			set: inst._xset.Bind( , {O:4, T:'int'})
		}
	}
	_xset(p, value) {
		NumPut(p.T, value, this, p.O)
	}
	__new() => super.__new(8, 0)
}

DllCall('GetCursorPos', 'ptr', pt := Point())
MsgBox pt.x ',' pt.y
pt.x := 1, pt.y := 2
MsgBox pt.x ',' pt.y
  • The properties are defined once only, for all instances of Point.
  • I've shown a few different ways to define the getter and setter.
  • There are no meta-functions, so you can assign new properties directly, if needed.
  • Some debugger clients can discover the x and y properties and list them for you, because they have been explicitly defined. (For instance, DebugVars and VSCode with vscode-autohotkey-debug will show you the values of x and y if you expand the object and its base, since that's where the getter/setter is defined.)
Of course, if you are hard-coding each property like this, you can define the properties directly in the class rather than with DefineProp. Conversely, the property name, offset and type can come from an array or object. For example, you can enumerate the properties of your {Mask: {O: 4, T: "UInt"}, ... } object with ObjOwnProps; you do not need to change the syntax of the object literal at all.
You are correct, It is for a class to use NumGet functions.
I got confused in the difference between map, arrays and objects.

I am trying to convert Class_RichEdit to V2. :facepalm: It is hard to convert a large complicated script that you hardly understand and functions you have never used, but I can try ...
(started form version from post viewtopic.php?f=6&t=59029&p=262862&hilit=richedit+v2#p262862)
in the Class PF2, "." and ":" are used as keys, but I assume that "attr" and "addr" are more logical.

I think I have fixed the VarSetCapacity functions and added "ErrorLevel := " before Sendmessage functions to fix some V2 Errors, but the Classes still need to be fixed. the __Set function of the class returns the undefined var [tabs], I assume this is an error in the original code.

My attempt for V2:

Code: Select all

	Class PF2 { ; PARAFORMAT2 structure -> http://msdn.microsoft.com/en-us/library/bb787942(v=vs.85).aspx
		__New() {
			Static PF2_Size := 188
			; this := {} ; not sure if this is needed
			this.attr := {Mask: {O: 4, T: "UInt"}, Numbering: {O: 8, T: "UShort"}
                         , StartIndent: {O: 12, T: "Int"}, RightIndent: {O: 16, T: "Int"}
                         , Offset: {O: 20, T: "Int"}, Alignment: {O: 24, T: "UShort"}
                         , TabCount: {O: 26, T: "UShort"}, Tabs: {O: 28, T: "UInt"}
                         , SpaceBefore: {O: 156, T: "Int"}, SpaceAfter: {O: 160, T: "Int"}
                         , LineSpacing: {O: 164, T: "Int"}, Style: {O: 168, T: "Short"}
                         , LineSpacingRule: {O: 170, T: "UChar"}, OutlineLevel: {O: 171, T: "UChar"}
                         , ShadingWeight: {O: 172, T: "UShort"}, ShadingStyle: {O: 174, T: "UShort"}
                         , NumberingStart: {O: 176, T: "UShort"}, NumberingStyle: {O: 178, T: "UShort"}
                         , NumberingTab: {O: 180, T: "UShort"}, BorderSpace: {O: 182, T: "UShort"}
                         , BorderWidth: {O: 184, T: "UShort"}, Borders: {O: 186, T: "UShort"}}
			This.Push("addr")
			This.SetCapacity("addr", PF2_Size)
			Addr :=  This.GetAddress("addr")
			DllCall("Kernel32.dll\RtlZeroMemory", "Ptr", Addr, "Ptr", PF2_Size)
			NumPut(PF2_Size, Addr + 0, 0, "UInt")
		}
		__Get(Name) {
			Addr := This.GetAddress("addr")
			If (Name = "PF2")
				Return Addr
			If !This.attr.HasKey(Name)
				Return ""
			Attr := This.attr.%Name%
			If (Name != "Tabs")
				Return NumGet(Addr + 0, Attr.O, Attr.T)
			Tabs := []
			Offset := Attr.O - 4
			Loop 32
				Tabs[A_Index] := NumGet(Addr + 0, Offset += 4, "UInt")
			Return Tabs
		}
		__Set(Name, Value) {
			Addr := This.GetAddress("addr")
			If !This.attr.HasKey(Name)
				Return ""
			Attr := This.attr.%Name%
			If (Name != "Tabs") {
				NumPut(Value, Addr + 0, Attr.O, Attr.T)
				Return Value
			}
			If !IsObject(Value)
				Return ""
			Offset := Attr.O - 4
			For Each, Tab In Value
				NumPut(Tab, Addr + 0, Offset += 4, "UInt")
			Return Tabs
		}
	}
Original Class V1:

Code: Select all

	Class PF2 { ; PARAFORMAT2 structure -> http://msdn.microsoft.com/en-us/library/bb787942(v=vs.85).aspx
		__New() {
			Static PF2_Size := 188
			ObjRawSet(this, ":", {Mask: {O: 4, T: "UInt"}, Numbering: {O: 8, T: "UShort"}
                         , StartIndent: {O: 12, T: "Int"}, RightIndent: {O: 16, T: "Int"}
                         , Offset: {O: 20, T: "Int"}, Alignment: {O: 24, T: "UShort"}
                         , TabCount: {O: 26, T: "UShort"}, Tabs: {O: 28, T: "UInt"}
                         , SpaceBefore: {O: 156, T: "Int"}, SpaceAfter: {O: 160, T: "Int"}
                         , LineSpacing: {O: 164, T: "Int"}, Style: {O: 168, T: "Short"}
                         , LineSpacingRule: {O: 170, T: "UChar"}, OutlineLevel: {O: 171, T: "UChar"}
                         , ShadingWeight: {O: 172, T: "UShort"}, ShadingStyle: {O: 174, T: "UShort"}
                         , NumberingStart: {O: 176, T: "UShort"}, NumberingStyle: {O: 178, T: "UShort"}
                         , NumberingTab: {O: 180, T: "UShort"}, BorderSpace: {O: 182, T: "UShort"}
                         , BorderWidth: {O: 184, T: "UShort"}, Borders: {O: 186, T: "UShort"}})
			This.Push(".")
			This.SetCapacity(".", PF2_Size)
			Addr :=  This.GetAddress(".")
			DllCall("Kernel32.dll\RtlZeroMemory", "Ptr", Addr, "Ptr", PF2_Size)
			NumPut(PF2_Size, Addr + 0, 0, "UInt")
		}
		__Get(Name) {
			Addr := This.GetAddress(".")
			If (Name = "PF2")
				Return Addr
			If !This[":"].HasKey(Name)
				Return ""
			Attr := This[":"][Name]
			If (Name != "Tabs")
				Return NumGet(Addr + 0, Attr.O, Attr.T)
			Tabs := []
			Offset := Attr.O - 4
			Loop 32
				Tabs[A_Index] := NumGet(Addr + 0, Offset += 4, "UInt")
			Return Tabs
		}
		__Set(Name, Value) {
			Addr := This.GetAddress(".")
			If !This[":"].HasKey(Name)
				Return ""
			Attr := This[":"][Name]
			If (Name != "Tabs") {
				NumPut(Value, Addr + 0, Attr.O, Attr.T)
				Return Value
			}
			If !IsObject(Value)
				Return ""
			Offset := Attr.O - 4
			For Each, Tab In Value
				NumPut(Tab, Addr + 0, Offset += 4, "UInt")
			Return Tabs
		}
	}

My current Class_RichEdit.ah2:

Code: Select all

; ======================================================================================================================
; Scriptname:     Class_RichEdit.ahk
; Namespace:      RichEdit
; Author:         just me
; AHK Version:    2.0-a100-52515e2 (AHK_User: Trying to convert it to 2.0-beta.1)
; OS Version:     Win 10 (x64)
; Function:       The class provides some wrapper functions for rich edit controls (v4.1 Unicode).
; Change History:
;    0.2.00.02    2021-09-04/AHK_User - Trying to convert it to 2.0-beta.1
;    0.2.00.01    2018-11-13/oif2003 - Added support for onEvent(), .Value, and support for:readonly, upper/lowercase
;    0.2.00.00    2018-11-08/oif2003 - Updated for AutoHotkey v2
;    0.1.05.00    2015-04-14/just me - fixed LoadRTF() not closing the file after reading
;    0.1.04.00    2014-08-27/just me - fixed SetParaIndent() and changed indentation sample
;    0.1.03.00    2014-03-03/just me - added GetTextRange()
;    0.1.02.00    2013-12-01/just me - changed SetText() to handle RTF formatted text properly
;    0.1.01.00    2013-11-29/just me - bug fix -> GetSelText()
;    0.1.00.00    2013-11-17/just me - initial beta release
; Credits:
;    corrupt for cRichEdit:
;       http://www.autohotkey.com/board/topic/17869-crichedit-standard-richedit-control-for-autohotkey-scripts/
;    jballi for HE_Print:
;       http://www.autohotkey.com/board/topic/45513-function-he-print-wysiwyg-print-for-the-hiedit-control/
;    majkinetor for Dlg:
;       http://www.autohotkey.com/board/topic/15836-module-dlg-501/
; ======================================================================================================================
Class RichEdit {
	; ===================================================================================================================
	; Class variables - do not change !!!
	; ===================================================================================================================
	; RichEdit v4.1 (Unicode)
	Static Class := "RICHEDIT50W"
	; RichEdit v4.1 (Unicode)
	Static DLL := "Msftedit.dll"
	; DLL handle
	Static Instance := DllCall("Kernel32.dll\LoadLibrary", "Str", RichEdit.DLL, "UPtr")
	; Callback function handling RichEdit messages
	Static SubclassCB := 0
	; Number of controls/instances
	Static Controls := 0
	
	
	; ===================================================================================================================
	; Instance variables and - do not change !!!
	; ===================================================================================================================
	DefFont := ""
	
	; GUI Control Properties
	Gui := ""
	Hwnd := ""
	ClassNN {
		get {
			return this.ctrl.ClassNN
		}
		set {
			this.ctrl.ClassNN := value
		}
	}
	Enabled {
		get {
			return this.ctrl.Enabled
		}
		set {
			this.ctrl.Enabled := value
		}
	}
	Focused {
		get {
			return this.ctrl.Focused
		}
	}
	Name {
		get {
			return this.ctrl.Name
		}
		set {
			this.ctrl.Name := value
		}
	}
	Pos {
		get {
			return this.ctrl.Pos
		}
	}
	Text {
		get {
			return this.GetText()
		}
		set {
			this.SetText(value)
		}
	}
	Type {
		get {
			return this.Type
		}
	}
	Value {
		get {
			return this.GetText()
		}
		set {
			this.SetText(value)
		}
	}
	Visible {
		get {
			return this.ctrl.Visible
		}
		set {
			this.ctrl.Visible := value
		}
	}
	
	;GUI Control Methods
	Move(Param*) {
		return this.ctrl.Move(Param*)
	}
	Focus() {
		return this.ctrl.Focus()
	}
	OnEvent(event := "", cbFunc := "", MaxThreads := 1) {
		static AhkChangeEventRegistered := false
		if event = "change" 
			this.onEventChangeCB := cbFunc
		else if event = "focus"
			this.onEventFocusCB := cbFunc
		else if event = "losefocus"
			this.onEventLoseFocusCB := cbFunc
		else return false
			if !AhkChangeEventRegistered {
				r := this.SetEventMask("Change")
				if isObject(cbFunc) && cbFunc.isBuiltIn != "" {
					this.onEventFunc := cbFunc
					OnMessage(0x111, (param*)=>this._onEventHandler(param*), MaxThreads) 
				}
				else r := false
					r := true, AhkChangeEventRegistered := true   
			}
		return r
	}
	_onEventHandler(wparam, lparam, msg, sender) {
		if msg == 0x111 {
			change := (wparam & 0xf000000) // 0x1000000
			if change != 4 && lparam == this.Hwnd {
				if change == 3 && this.onEventChangeCB != ""
					this.onEventChangeCB.Call(wparam, lparam, msg, sender)
				else if change == 1 && this.onEventFocusCB != ""
					this.onEventFocusCB.Call(wparam, lparam, msg, sender)
				else if change == 2 && this.onEventLoseFocusCB != ""
					this.onEventLoseFocusCB.Call(wparam, lparam, msg, sender)
			}
		}
	}
	Opt(param*) {
		this.Options(param*)
	}
	Options(options := "", mode := "SET") {
		static reOptionsList := { AUTOWORDSELECTION: 0x01, AUTOVSCROLL: 0x40, AUTOHSCROLL: 0x80
                           , NOHIDESEL: 0x100, READONLY: 0x800, WANTRETURN: 0x1000
                           , SAVESEL: 0x8000, SELECTIONBAR: 0x01000000, VERTICAL: 0x400000}
		options := StrSplit(options, " ")
		
		for _, v in options
			if reOptionsList.HasKey(v)
				reOptions .= v " "
		else
            _options .= v " "
		
		this.ctrl.Options(_options)
		this.SetOptions(Trim(reOptions, " `t`n`r"), mode)
	}
	
	; ===================================================================================================================
	; CONSTRUCTOR
	; ===================================================================================================================
	__New(Gui, Options := "", DefaultText := "", MultiLine := True) {
		Static WS_TABSTOP := 0x10000, WS_HSCROLL := 0x100000, WS_VSCROLL := 0x200000, WS_VISIBLE := 0x10000000
           , WS_CHILD := 0x40000000, WS_BORDER := 0x800000
           , WS_EX_CLIENTEDGE := 0x200, WS_EX_STATICEDGE := 0x20000
           , ES_MULTILINE := 0x0004, ES_AUTOVSCROLL := 0x40, ES_AUTOHSCROLL := 0x80, ES_NOHIDESEL := 0x0100
           , ES_WANTRETURN := 0x1000, ES_DISABLENOSCROLL := 0x2000, ES_SUNKEN := 0x4000, ES_SAVESEL := 0x8000
           , ES_SELECTIONBAR := 0x1000000
		
		;list of option keyword and [funcName*, param*] pairs. Where funcName is an array that stores the classes and method,
		;ie : ["RichEdit, "CallBack", "someMethod"]; and param* is a variadic array of parameters to be passed
		;We will call these at the end of the constructor to set options found in array reOptions
		Static optionsList := Map("-WRAP", [["WordWrap"], [false]], "+WRAP", [["WordWrap"], [true]], "readonly", [["SetOptions"], ["readonly"]]
                              ,"lowercase", [["SetStyles"], ["lowercase"]] ,"uppercase", [["SetStyles"], ["uppercase"]]) ;SetOptions(Options := ""SetStyles
		
		; Do not instantiate instances of RichEdit
		If (This.Base.HWND)
			Return False
		
		If !(Gui) {
			ErrorLevel := "ERROR: Gui does not exist!"
			Return False
		}
		this.gui := gui
		
		; Load library if necessary
		If (This.Base.Instance == 0) {
			This.Base.Instance := DllCall("Kernel32.dll\LoadLibrary", "Str", This.Base.DLL, "UPtr")
			If (ErrorLevel) {
				ErrorLevel := "ERROR: Error loading " . This.Base.DLL . " - " . ErrorLevel
				Return False
			}
		}
		; Specify default styles & exstyles
		Styles := WS_TABSTOP | WS_VISIBLE | WS_CHILD | ES_AUTOHSCROLL ;| WS_BORDER
		If (MultiLine)
			Styles |= WS_VSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_NOHIDESEL | ES_WANTRETURN ; | WS_HSCROLL
		| ES_DISABLENOSCROLL | ES_SAVESEL ; | ES_SELECTIONBAR does not work properly
		ExStyles := WS_EX_STATICEDGE
		; Create the control
		CtrlClass := This.Class
		
		;separate standard AHK control options and RE options (which we will set at the end)
		reOptions := [], ctrlOptions := []
		_options := StrSplit(Options, " ")
		for _, v in _options {
			if optionsList.HasKey(v)
				reOptions.Push(optionsList[v])
			else
				ctrlOptions.Push(v)
		}
		Options := ""
		for k, v in ctrlOptions
			Options .= (k == 1 ? "" : " ") v
		
		ctrl := Gui.Add("Custom", "Class" CtrlClass " " Options " +" Styles " +E" ExStyles)
		this.ctrl := ctrl
		HWND := ctrl.hwnd
		If (MultiLine) {
			; Adjust the formatting rectangle for multiline controls to simulate a selection bar
			; EM_GETRECT = 0xB2, EM_SETRECT = 0xB3
			;v1 VarSetCapacity(RECT, 16, 0)
			RECT := Buffer(16, 0)
			SendMessage(0xB2, 0, &RECT, , "ahk_id" HWND)
			NumPut(NumGet(RECT, 0, "Int") + 1, RECT, 0, "Int")
			NumPut(NumGet(RECT, 4, "Int") + 2,  RECT, 4, "Int")
			SendMessage(0xB3, 0, &RECT, , "ahk_id" HWND)
			; Set advanced typographic options
			; EM_SETTYPOGRAPHYOPTIONS = 0x04CA (WM_USER + 202)
			; TO_ADVANCEDTYPOGRAPHY	= 1, TO_ADVANCEDLAYOUT = 8 ? not documented
			SendMessage(0x04CA, 0x01, 0x01, , "ahk_id" HWND)
		}
		; Initialize control
		; EM_SETLANGOPTIONS = 0x0478 (WM_USER + 120)
		; IMF_AUTOKEYBOARD = 0x01, IMF_AUTOFONT = 0x02
		SendMessage(0x0478, 0, 0x03, , "ahk_id" HWND)
		; Subclass the control to get Tab key and prevent Esc from sending a WM_CLOSE message to the parent window.
		; One of majkinetor's splendid discoveries!
		; Initialize SubclassCB
		If (This.Base.SubclassCB = 0)
			This.Base.SubclassCB := CallbackCreate("RichEdit.SubclassProc")
		DllCall("Comctl32.dll\SetWindowSubclass", "Ptr", HWND, "Ptr", This.Base.SubclassCB, "Ptr", HWND, "Ptr", 0)
		
		This.HWND := HWND
		This.DefFont := This.GetFont(1)
		This.DefFont.Default := 1
		; Correct AHK font size setting, if necessary
		If (Round(This.DefFont.Size) != This.DefFont.Size) {
			This.DefFont.Size := Round(This.DefFont.Size)
			This.SetDefaultFont()
		}
		This.Base.Controls += 1
		; Initialize the print margins
		;This.GetMargins()
		; Initialize the text limit
		This.LimitText(2147483647)
		
		;IRichEditOleCallback interface
		;https://docs.microsoft.com/en-us/windows/desktop/api/richole/nn-richole-iricheditolecallback
		this.Callback.SetOleCallback(hwnd)
		
		;Setting options for RichEdit
		;Conditional options / defaults
		if MultiLine
            this.WordWrap(true)
		if DefaultText != ""
            this.text := DefaultText
		
		;RichEdit options
		for _, v in reOptions
            if v[1].Length() == 1
				this[v[1][1]](v[2]*)
		else {
			obj := this, i := 1
			while i < v[1].Length()
				obj := obj[ v[1][i++] ]
			obj[ v[1][i++] ](v[2]*)
		}
	}
	
	class CallBack {
		; Authors:        just me & DigiDon
		; Description:    IRichEditOleCallback interface AHK implementation for the RichEdit control
		Static ContextMenu := ""
		makeContextMenu() {
			ContextMenu := Menu()    ; Simple context menu
			ContextMenu.Add("&Copy", ()=>Send("^c"))
			ContextMenu.Add("&Paste", ()=>Send("^v"))
			ContextMenu.Add("Cu&t", ()=>Send("^x"))
			ContextMenu.Add("Select &All", ()=>Send("^a"))
			ContextMenu.Add("&Undo", ()=>Send("^z"))
			ContextMenu.Add("&Redo", ()=>Send("^y"))
			this.ContextMenu := ContextMenu
		}
		
		SetOleCallback(hwnd) {
			static cb := RichEdit.Callback
			static VTBL := [CallbackCreate((p1, p2, p3, p4) => cb.IREOleCB_QueryInterface(p1, p2, p3, p4))
                       , CallbackCreate((p1) => cb.IREOleCB_AddRef(p1))
                       , CallbackCreate((p1) => cb.IREOleCB_Release(p1))
                       , CallbackCreate((p1, p2) => cb.IREOleCB_GetNewStorage(p1, p2))
                       , CallbackCreate((p1, p2, p3, p4) => cb.IREOleCB_GetInPlaceContext(p1, p2, p3, p4))
                       , CallbackCreate((p1, p2) => cb.IREOleCB_ShowContainerUI(p1, p2))
                       , CallbackCreate((p1, p2, p3, p4) => cb.IREOleCB_QueryInsertObject(p1, p2, p3, p4))
                       , CallbackCreate((p1, p2) => cb.IREOleCB_DeleteObject(p1, p2))
                       , CallbackCreate((p1, p2, p3, p4, p5, p6) => cb.IREOleCB_QueryAcceptData(p1, p2, p3, p4, p5, p6))
                       , CallbackCreate((p1, p2) => cb.IREOleCB_ContextSensitiveHelp(p1, p2))
                       , CallbackCreate((p1, p2, p3, p4) => cb.IREOleCB_GetClipboardData(p1, p2, p3, p4))
                       , CallbackCreate((p1, p2, p3, p4) => cb.IREOleCB_GetDragDropEffect(p1, p2, p3, p4))
                       , CallbackCreate((p1, p2, p3, p4, p5) => cb.IREOleCB_GetContextMenu(p1, p2, p3, p4, p5))]
			
			;this.RE_SetOleCallback(this.Hwnd)
			HeapSize := A_PtrSize * 20 ; VTBL pointer + 13 method pointers + 4 unused pointers + reference count + HEAP handle
			HeapOffset := A_PtrSize * 19 ; offset to store the heap handle within the heap
			Heap := DllCall("HeapCreate", "UInt", 0x05, "Ptr", 0, "Ptr", 0, "UPtr")
			IREOleCB := DllCall("HeapAlloc", "Ptr", Heap, "UInt", 0x08, "Ptr", HeapSize, "UPtr")
			Addr := IREOleCB
			Addr := NumPut(Addr + A_PtrSize, Addr + 0, "UPtr")
			For Each, CB In VTBL
				Addr := NumPut(CB, Addr + 0, "UPtr")
			NumPut(Heap, IREOleCB + HeapOffset, "UPtr")
			SendMessage( 0x0446 , 0, IREOleCB , , "ahk_id" HWND)
		}
		
		; --------------------------------------------------------------------------------------------------------------------------------
		IREOleCB_QueryInterface(IREOleCB, REFIID, &IFPtr) {  ; IUnknown::QueryInterface
			OutputDebug(A_ThisFunc)
			Return 0 ; S_OK
		}
		
		; --------------------------------------------------------------------------------------------------------------------------------
		IREOleCB_AddRef(IREOleCB) {   ; IUnknown::AddRef
			Static RefOffset := A_PtrSize * 18
			OutputDebug(A_ThisFunc)
			NumPut(RefCount := NumGet(IREOleCB + RefOffset, "UInt") + 1, IREOleCB + RefOffset, "UInt")
			Return RefCount
		}
		
		; --------------------------------------------------------------------------------------------------------------------------------
		IREOleCB_Release(IREOleCB) {  ; IUnknown::Release
			Static RefOffset := A_PtrSize * 18
              , HeapOffset := A_PtrSize * 19
			OutputDebug(A_ThisFunc)
			NumPut(RefCount := NumGet(IREOleCB + RefOffset, "UInt") - 1, IREOleCB + RefOffset, "UInt")
			If (RefCount = 0) {
				Heap := NumGet(IREOleCB + HeapOffset, "UPtr")
				DllCall("HeapDestroy", "Ptr", Heap)
			}
			Return RefCount
		}
		
		; --------------------------------------------------------------------------------------------------------------------------------
		IREOleCB_GetNewStorage(IREOleCB, IStoragePtr) {   ; IRichEditOleCallback::GetNewStorage
			OutputDebug(A_ThisFunc)
			; msdn.microsoft.com/en-us/library/windows/desktop/aa378977(v=vs.85).aspx
			If !(HR := DllCall("Ole32.dll\CreateILockBytesOnHGlobal", "Ptr", 0, "Int", 1, "PtrP", &ILockBytes)) {
				; msdn.microsoft.com/en-us/library/windows/desktop/aa380324(v=vs.85).aspx
				; STGM_READWRITE = 0x02, STGM_SHARE_EXCLUSIVE = 0x10, STGM_CREATE = 0x1000
				If (HR := DllCall("Ole32.dll\StgCreateDocfileOnILockBytes", "Ptr", ILockBytes, "UInt", 0x1012, "UInt", 0, "PtrP", &IStorage))
					ObjRelease(ILockBytes)
				Else
					NumPut(IStorage, IStoragePtr + 0, "UPtr")
			}
			Return HR
		}
		
		; --------------------------------------------------------------------------------------------------------------------------------
		IREOleCB_GetInPlaceContext(IREOleCB, Frame, Doc, FrameInfo) { ; IRichEditOleCallback::GetInPlaceContext - not implemented
			OutputDebug(A_ThisFunc)
			Return 0x80004001 ; E_NOTIMPL
		}
		
		; --------------------------------------------------------------------------------------------------------------------------------
		IREOleCB_ShowContainerUI(IREOleCB, Show) { ; IRichEditOleCallback::ShowContainerUI - not implemented
			OutputDebug(A_ThisFunc)
			Return 0x80004001 ; E_NOTIMPL
		}
		
		; --------------------------------------------------------------------------------------------------------------------------------
		IREOleCB_QueryInsertObject(IREOleCB, CLSID, STG, CP) { ; IRichEditOleCallback::QueryInsertObject - returns S_OK
			OutputDebug(A_ThisFunc)
			Return 0 ; S_OK
		}
		; --------------------------------------------------------------------------------------------------------------------------------
		
		; --------------------------------------------------------------------------------------------------------------------------------
		IREOleCB_DeleteObject(IREOleCB, OleObj) { ; IRichEditOleCallback::DeleteObject - returns S_OK
			OutputDebug(A_ThisFunc)
			Return 0 ; S_OK
		}
		
		; --------------------------------------------------------------------------------------------------------------------------------
		IREOleCB_QueryAcceptData(IREOleCB, DataObj, Format, Operation, Really, MetaPic) { ; IRichEditOleCallback::QueryAcceptData - returns S_OK
			OutputDebug(A_ThisFunc)
			Return 0 ; S_OK
		}
		
		; --------------------------------------------------------------------------------------------------------------------------------
		IREOleCB_ContextSensitiveHelp(IREOleCB, EnterMode) {  ; IRichEditOleCallback::ContextSensitiveHelp - not implemented
			OutputDebug(A_ThisFunc)
			Return 0x80004001 ; E_NOTIMPL
		}
		
		; --------------------------------------------------------------------------------------------------------------------------------
		IREOleCB_GetClipboardData(IREOleCB, CharRange, Operation, DataObj) { ; IRichEditOleCallback::GetClipboardData - not implemented
			Return 0x80004001 ; E_NOTIMPL
		}
		
		; --------------------------------------------------------------------------------------------------------------------------------
		IREOleCB_GetDragDropEffect(IREOleCB, Drag, KeyState, Effect) {  ; IRichEditOleCallback::GetDragDropEffect - returns S_OK
			Return 0 ; S_OK
		}
		
		; --------------------------------------------------------------------------------------------------------------------------------
		IREOleCB_GetContextMenu(IREOleCB, SelType, OleObj, CharRange, HMENU) { ; IRichEditOleCallback::GetContextMenu 
			if this.ContextMenu == ""
				this.makeContextMenu()
			; GCM_RIGHTMOUSEDROP = 0x8000
			; OutputDebug(A_ThisFunc)
			this.ContextMenu.Show()
			If !(SelType & 0x8000) { ; indicates that a context menu for a right-mouse drag drop should be generated
				MouseGetPos( , , &HGUI, &HRE, 2)
				DllCall("PostMessage", "Ptr", HGUI, "UInt", 0x007B, "Ptr", HRE, "Ptr", -1)
			}
			Return 0
		}
	}
	
	
	
	
	; ===================================================================================================================
	; DESTRUCTOR
	; ===================================================================================================================
	__Delete() {
		If (This.HWND) {
			DllCall("Comctl32.dll\RemoveWindowSubclass", "Ptr", This.HWND, "Ptr", This.Base.SubclassCB, "Ptr", 0)
			DllCall("User32.dll\DestroyWindow", "Ptr", This.HWND)
			This.HWND := 0
			This.Base.Controls -= 1
			If (This.Base.Controls = 0) {
				DllCall("Kernel32.dll\FreeLibrary", "Ptr", This.Base.Instance)
				This.Base.Instance := 0
			}
		}
	}
	; ===================================================================================================================
	; ===================================================================================================================
	; INTERNAL CLASSES ==================================================================================================
	; ===================================================================================================================
	; ===================================================================================================================
	Class CF2 { ; CHARFORMAT2 structure -> http://msdn.microsoft.com/en-us/library/bb787883(v=vs.85).aspx
		__New() {
			Static CF2_Size := 116
			ObjRawSet(this, ":", {Mask: {O: 4, T: "UInt"}, Effects: {O: 8, T: "UInt"}
                         , Height: {O: 12, T: "Int"}, Offset: {O: 16, T: "Int"}
                         , TextColor: {O: 20, T: "Int"}, CharSet: {O: 24, T: "UChar"}
                         , PitchAndFamily: {O: 25, T: "UChar"}, FaceName: {O: 26, T: "Str32"}
                         , Weight: {O: 90, T: "UShort"}, Spacing: {O: 92, T: "Short"}
                         , BackColor: {O: 96, T: "UInt"}, LCID: {O: 100, T: "UInt"}
                         , Cookie: {O: 104, T: "UInt"}, Style: {O: 108, T: "Short"}
                         , Kerning: {O: 110, T: "UShort"}, UnderlineType: {O: 112, T: "UChar"}
                         , Animation: {O: 113, T: "UChar"}, RevAuthor: {O: 114, T: "UChar"}
                         , UnderlineColor: {O: 115, T: "UChar"}})
			This.Push(".")
			This.SetCapacity(".", CF2_Size)
			Addr :=  This.GetAddress(".")
			DllCall("Kernel32.dll\RtlZeroMemory", "Ptr", Addr, "Ptr", CF2_Size)
			NumPut(CF2_Size, Addr + 0, 0, "UInt")
		}
		__Get(Name) {
			Addr := This.GetAddress(".")
			If (Name = "CF2")
				Return Addr
			If !This[":"].HasKey(Name)
				Return ""
			Attr := This[":"][Name]
			If (Name != "FaceName")
				Return NumGet(Addr + 0, Attr.O, Attr.T)
			Return StrGet(Addr + Attr.O, 32)
		}
		__Set(Name, Value) {
			Addr := This.GetAddress(".")
			If !This[":"].HasKey(Name)
				Return ""
			Attr := This[":"][Name]
			If (Name != "FaceName")
				NumPut(Value, Addr + 0, Attr.O, Attr.T)
			Else
				StrPut(Value, Addr + Attr.O, 32)
			Return Value
		}
	}
	; -------------------------------------------------------------------------------------------------------------------
	Class PF2 { ; PARAFORMAT2 structure -> http://msdn.microsoft.com/en-us/library/bb787942(v=vs.85).aspx
		__New() {
			Static PF2_Size := 188
			ObjRawSet(this, ":", {Mask: {O: 4, T: "UInt"}, Numbering: {O: 8, T: "UShort"}
                         , StartIndent: {O: 12, T: "Int"}, RightIndent: {O: 16, T: "Int"}
                         , Offset: {O: 20, T: "Int"}, Alignment: {O: 24, T: "UShort"}
                         , TabCount: {O: 26, T: "UShort"}, Tabs: {O: 28, T: "UInt"}
                         , SpaceBefore: {O: 156, T: "Int"}, SpaceAfter: {O: 160, T: "Int"}
                         , LineSpacing: {O: 164, T: "Int"}, Style: {O: 168, T: "Short"}
                         , LineSpacingRule: {O: 170, T: "UChar"}, OutlineLevel: {O: 171, T: "UChar"}
                         , ShadingWeight: {O: 172, T: "UShort"}, ShadingStyle: {O: 174, T: "UShort"}
                         , NumberingStart: {O: 176, T: "UShort"}, NumberingStyle: {O: 178, T: "UShort"}
                         , NumberingTab: {O: 180, T: "UShort"}, BorderSpace: {O: 182, T: "UShort"}
                         , BorderWidth: {O: 184, T: "UShort"}, Borders: {O: 186, T: "UShort"}})
			This.Push(".")
			This.SetCapacity(".", PF2_Size)
			Addr :=  This.GetAddress(".")
			DllCall("Kernel32.dll\RtlZeroMemory", "Ptr", Addr, "Ptr", PF2_Size)
			NumPut(PF2_Size, Addr + 0, 0, "UInt")
		}
		__Get(Name) {
			Addr := This.GetAddress(".")
			If (Name = "PF2")
				Return Addr
			If !This[":"].HasKey(Name)
				Return ""
			Attr := This[":"][Name]
			If (Name != "Tabs")
				Return NumGet(Addr + 0, Attr.O, Attr.T)
			Tabs := []
			Offset := Attr.O - 4
			Loop 32
				Tabs[A_Index] := NumGet(Addr + 0, Offset += 4, "UInt")
			Return Tabs
		}
		__Set(Name, Value) {
			Addr := This.GetAddress(".")
			If !This[":"].HasKey(Name)
				Return ""
			Attr := This[":"][Name]
			If (Name != "Tabs") {
				NumPut(Value, Addr + 0, Attr.O, Attr.T)
				Return Value
			}
			If !IsObject(Value)
				Return ""
			Offset := Attr.O - 4
			For Each, Tab In Value
				NumPut(Tab, Addr + 0, Offset += 4, "UInt")
			Return Tabs
		}
	}
	; ===================================================================================================================
	; ===================================================================================================================
	; PRIVATE METHODS ===================================================================================================
	; ===================================================================================================================
	; ===================================================================================================================
	GetBGR(RGB) { ; Get numeric BGR value from numeric RGB value or HTML color name
		Static HTML := {BLACK:  0x000000, SILVER: 0xC0C0C0, GRAY:   0x808080, WHITE:   0xFFFFFF
                    , MAROON: 0x000080, RED:    0x0000FF, PURPLE: 0x800080, FUCHSIA: 0xFF00FF
                    , GREEN:  0x008000, LIME:   0x00FF00, OLIVE:  0x008080, YELLOW:  0x00FFFF
                    , NAVY:   0x800000, BLUE:   0xFF0000, TEAL:   0x808000, AQUA:    0xFFFF00}
		If HTML.HasKey(RGB)
			Return HTML[RGB]
		Return ((RGB & 0xFF0000) >> 16) + (RGB & 0x00FF00) + ((RGB & 0x0000FF) << 16)
	}
	; -------------------------------------------------------------------------------------------------------------------
	GetRGB(BGR) {  ; Get numeric RGB value from numeric BGR-Value
		Return ((BGR & 0xFF0000) >> 16) + (BGR & 0x00FF00) + ((BGR & 0x0000FF) << 16)
	}
	; -------------------------------------------------------------------------------------------------------------------
	GetMeasurement() { ; Get locale measurement (metric / inch)
		; LOCALE_USER_DEFAULT = 0x0400, LOCALE_IMEASURE = 0x0D, LOCALE_RETURN_NUMBER = 0x20000000
		Static Metric := 2.54  ; centimeters
           , Inches := 1.00  ; inches
           , Measurement := ""
           , Len := 2 ;Changed
        ;Old   , Len := A_IsUnicode ? 2 : 4
		
		If (Measurement = "") {
			;OldVarSetCapacity(LCD, 4, 0)
			LCD := Buffer(4, 0)
			DllCall("Kernel32.dll\GetLocaleInfo", "UInt", 0x400, "UInt", 0x2000000D, "Ptr", &LCD, "Int", Len)
			Measurement := NumGet(LCD, 0, "UInt") ? Inches : Metric
		}
		Return Measurement
	}
	; -------------------------------------------------------------------------------------------------------------------
	SubclassProc(M, W, L, I, R) { ; RichEdit subclassproc
		; Left out first parameter HWND, will be found in "This" when called by system
		; See -> http://msdn.microsoft.com/en-us/library/bb776774%28VS.85%29.aspx
		If (M == 0x87) ; WM_GETDLGCODE
			Return 4   ; DLGC_WANTALLKEYS
		Return DllCall("Comctl32.dll\DefSubclassProc", "Ptr", This, "UInt", M, "Ptr", W, "Ptr", L)
	}
	; ===================================================================================================================
	; ===================================================================================================================
	; PUBLIC METHODS ====================================================================================================
	; ===================================================================================================================
	; ===================================================================================================================
	; -------------------------------------------------------------------------------------------------------------------
	; Methods to be used by advanced users only
	; -------------------------------------------------------------------------------------------------------------------
	GetCharFormat() { ; Retrieves the character formatting of the current selection.
		; For details see http://msdn.microsoft.com/en-us/library/bb787883(v=vs.85).aspx.
		; Returns a 'CF2' object containing the formatting settings.
		; EM_GETCHARFORMAT = 0x043A
		CF2 := New This.CF2
		SendMessage(0x043A, 1, CF2.CF2, , "ahk_id "  This.HWND)
		Return (CF2.Mask ? CF2 : False)
	}
	; -------------------------------------------------------------------------------------------------------------------
	SetCharFormat(CF2) { ; Sets character formatting of the current selection.
		; For details see http://msdn.microsoft.com/en-us/library/bb787883(v=vs.85).aspx.
		; CF2 : CF2 object like returned by GetCharFormat().
		; EM_SETCHARFORMAT = 0x0444
		ErrorLevel := SendMessage( 0x0444, 1,  CF2.CF2, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	GetParaFormat() { ; Retrieves the paragraph formatting of the current selection.
		; For details see http://msdn.microsoft.com/en-us/library/bb787942(v=vs.85).aspx.
		; Returns a 'PF2' object containing the formatting settings.
		; EM_GETPARAFORMAT = 0x043D
		PF2 := New This.PF2
		ErrorLevel := SendMessage(0x043D, 0, PF2.PF2, , "ahk_id " . This.HWND)
		Return (PF2.Mask ? PF2 : False)
	}
	; -------------------------------------------------------------------------------------------------------------------
	SetParaFormat(PF2) { ; Sets the  paragraph formatting for the current selection.
		; For details see http://msdn.microsoft.com/en-us/library/bb787942(v=vs.85).aspx.
		; PF2 : PF2 object like returned by GetParaFormat().
		; EM_SETPARAFORMAT = 0x0447
		ErrorLevel := SendMessage(0x0447, 0, PF2.PF2, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	; Control specific
	; -------------------------------------------------------------------------------------------------------------------
	IsModified() { ; Has the control been  modified?
		; EM_GETMODIFY = 0xB8
		ErrorLevel := SendMessage( 0xB8, 0, 0, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	SetModified(Modified := False) { ; Sets or clears the modification flag for an edit control.
		; EM_SETMODIFY = 0xB9
		ErrorLevel := SendMessage( 0xB9, !!Modified, 0, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	SetEventMask(Events := "") { ; Set the events which shall send notification codes control's owner
		; Events : Array containing one or more of the keys defined in 'ENM'.
		; For details see http://msdn.microsoft.com/en-us/library/bb774238(v=vs.85).aspx
		; EM_SETEVENTMASK	= 	0x0445
		Static ENM := {NONE: 0x00, CHANGE: 0x01, UPDATE: 0x02, SCROLL: 0x04, SCROLLEVENTS: 0x08, DRAGDROPDONE: 0x10
                   , PARAGRAPHEXPANDED: 0x20, PAGECHANGE: 0x40, KEYEVENTS: 0x010000, MOUSEEVENTS: 0x020000
                   , REQUESTRESIZE: 0x040000, SELCHANGE: 0x080000, DROPFILES: 0x100000, PROTECTED: 0x200000
                   , LINK: 0x04000000}
		
		if Events is "Integer"
			Mask := Events
		else {
			Mask := 0
			Events := StrSplit(Events, " ")
			for _, v in Events {
				If ENM.HasKey(v) {
					Mask := Mask | ENM[v]
				}
				Else
					Return False
			}
		}
		
		r := SendMessage(0x0445, 0, Mask, , "ahk_id " . This.HWND)
		return r
	}
	; -------------------------------------------------------------------------------------------------------------------
	; Loading and storing RTF format
	; -------------------------------------------------------------------------------------------------------------------
	GetRTF(Selection := False) { ; Gets the whole content of the control as rich text.
		; Selection = False : whole contents (default)
		; Selection = True  : current selection
		; EM_STREAMOUT = 0x044A
		; SF_TEXT = 0x1, SF_RTF = 0x2, SF_RTFNOOBJS = 0x3, SF_UNICODE = 0x10, SF_USECODEPAGE =	0x0020
		; SFF_PLAINRTF = 0x4000, SFF_SELECTION = 0x8000
		; UTF-8 = 65001, UTF-16 = 1200
		Static GetRTFCB := 0
		Flags := 0x4022 | (1200 << 16) | (Selection ? 0x8000 : 0)
		GetRTFCB := CallbackCreate("RichEdit.GetRTFProc")
		;Old VarSetCapacity(ES, (A_PtrSize * 2) + 4, 0) ; EDITSTREAM structure
		ES := Buffer((A_PtrSize * 2) + 4, 0) ; EDITSTREAM structure ;Changed
		NumPut(This.HWND, ES, 0, "Ptr")            ; dwCookie
		NumPut(GetRTFCB, ES, A_PtrSize + 4, "Ptr") ; pfnCallback
		SendMessage(0x044A, Flags, &ES, , "ahk_id " . This.HWND)
		DllCall("Kernel32.dll\GlobalFree", "Ptr", GetRTFCB)
		Return This.GetRTFProc("Get", 0, 0)
	}
	; -------------------------------------------------------------------------------------------------------------------
	GetRTFProc(pbBuff, cb, pcb) { ; Callback procedure for GetRTF
		; left out first parameter dwCookie, will be passed in "This" when called by system
		Static RTF := ""
		If (cb > 0) {
			RTF .= StrGet(pbBuff, cb, "CP0")
			Return 0
		}
		If (pbBuff = "Get") {
			Out := RTF
			;Old VarSetCapacity(RTF, 0)
			VarSetStrCapacity(&RTF, 0) ; Changed
			Return Out
		}
		Return 1
	}
	; -------------------------------------------------------------------------------------------------------------------
	LoadRTF(FilePath, Selection := False) { ; Loads RTF file into the control.
		; FilePath = file path
		; Selection = False : whole contents (default)
		; Selection = True  : current selection
		; EM_STREAMIN = 0x0449
		; SF_TEXT = 0x1, SF_RTF = 0x2, SF_RTFNOOBJS = 0x3, SF_UNICODE = 0x10, SF_USECODEPAGE =	0x0020
		; SFF_PLAINRTF = 0x4000, SFF_SELECTION = 0x8000
		; UTF-16 = 1200
		Static LoadRTFCB := CallbackCreate("RichEdit.LoadRTFProc")
		Flags := 0x4002 | (Selection ? 0x8000 : 0) ; | (1200 << 16)
		If !(File := FileOpen(FilePath, "r"))
			Return False
		;Old VarSetCapacity(ES, (A_PtrSize * 2) + 4, 0)  ; EDITSTREAM structure
		ES := Buffer((A_PtrSize * 2) + 4, 0)  ; EDITSTREAM structure ;Changed
		NumPut(File.__Handle, ES, 0, "Ptr")         ; dwCookie
		NumPut(LoadRTFCB, ES, A_PtrSize + 4, "Ptr") ; pfnCallback
		ErrorLevel := SendMessage(0x0449, Flags, &ES, ,  "ahk_id " . This.HWND)
		Result := ErrorLevel
		File.Close()
		Return Result
	}
	; -------------------------------------------------------------------------------------------------------------------
	LoadRTFProc(pbBuff, cb, pcb) { ; Callback procedure for LoadRTF
		; Left out first parameter dwCookie, will be passed in "This" when called by system
		Return !DllCall("ReadFile", "Ptr", This, "Ptr", pbBuff, "UInt", cb, "Ptr", pcb, "Ptr", 0)
	}
	; -------------------------------------------------------------------------------------------------------------------
	; Scrolling
	; -------------------------------------------------------------------------------------------------------------------
	GetScrollPos() { ; Obtains the current scroll position.
		; Returns on object with keys 'X' and 'Y' containing the scroll position.
		; EM_GETSCROLLPOS = 0x04DD
		;Old VarSetCapacity(PT, 8, 0)
		PT := Buffer(8, 0) ; Changed
		SendMessage(0x04DD, 0, &PT, , "ahk_id " . This.HWND)
		Return {X: NumGet(PT, 0, "Int"), Y: NumGet(PT, 4, "Int")}
	}
	; -------------------------------------------------------------------------------------------------------------------
	SetScrollPos(X, Y) { ; Scrolls the contents of a rich edit control to the specified point.
		; X : x-position to scroll to.
		; Y : y-position to scroll to.
		; EM_SETSCROLLPOS = 0x04DE
		;Old VarSetCapacity(PT, 8, 0)
		PT := Buffer(8, 0) ; Changed
		NumPut(X, PT, 0, "Int")
		NumPut(Y, PT, 4, "Int")
		ErrorLevel := SendMessage(0x04DE, 0, &PT, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	ScrollCaret() { ; Scrolls the caret into view.
		; EM_SCROLLCARET = 0x00B7
		SendMessage(0x00B7, 0, 0, , "ahk_id " . This.HWND)
		Return True
	}
	; -------------------------------------------------------------------------------------------------------------------
	ShowScrollBar(SB, Mode := True) { ; Shows or hides one of the scroll bars of a rich edit control.
		; SB   : Identifies which scroll bar to display: horizontal or vertical.
		;        This parameter must be 1 (SB_VERT) or 0 (SB_HORZ).
		; Mode : Specify TRUE to show the scroll bar and FALSE to hide it.
		; EM_SHOWSCROLLBAR = 0x0460 (WM_USER + 96)
		SendMessage(0x0460, SB, Mode, , "ahk_id " . This.HWND)
		Return True
	}
	; -------------------------------------------------------------------------------------------------------------------
	; Text and selection
	; -------------------------------------------------------------------------------------------------------------------
	RegExFunc(needle, function) {
		if isObject(function) && function.isBuiltIn != "" {
			textLen := this.GetTextLen(), text := this.GetText(), sp := 0, len := 1, match := []
			while sp < textLen && sp := RegExMatch(text, needle, &m, sp + len)
				len := m.len > 0 ? m.len : 1
            , _match := SubStr(text, sp, m.Len)
            , match.push(_match)
            , function.Call(_match, sp, len)
			return match
		}
		else
			return false
	}
	
	FindText(Find, Mode := "") { ; Finds Unicode text within a rich edit control.
		; Find : Text to search for.
		; Mode : Optional array containing one or more of the keys specified in 'FR'.
		;        For details see http://msdn.microsoft.com/en-us/library/bb788013(v=vs.85).aspx.
		; Returns True if the text was found; otherwise false.
		; EM_FINDTEXTEXW = 0x047C, EM_SCROLLCARET = 0x00B7
		Static FR:= {DOWN: 1, WHOLEWORD: 2, MATCHCASE: 4}
		Flags := 0
		if Mode != ""
			For Each, Value In Mode
				If FR.HasKey(Value)
					Flags |= FR[Value]
		Sel := This.GetSel()
		Min := (Flags & FR.DOWN) ? Sel.E : Sel.S
		Max := (Flags & FR.DOWN) ? -1 : 0
		;Old VarSetCapacity(FTX, 16 + A_PtrSize, 0)
		FTX := Buffer(16 + A_PtrSize, 0) ;Changed 
		NumPut(Min, FTX, 0, "Int")
		NumPut(Max, FTX, 4, "Int")
		NumPut(&Find, FTX, 8, "Ptr")
		SendMessage(0x047C, Flags, &FTX, , "ahk_id " . This.HWND)
		S := NumGet(FTX, 8 + A_PtrSize, "Int"), E := NumGet(FTX, 12 + A_PtrSize, "Int")
		If (S = -1) && (E = -1)
			Return False
		This.SetSel(S, E)
		This.ScrollCaret()
		Return
	}
	; ------------------------------------------------------------------------------------------------------------------- 
	GotoPos(pos) {           ;Goto position
		This.SetSel(pos, pos)
		This.ScrollCaret()
	}
	; -------------------------------------------------------------------------------------------------------------------
	FindWordBreak(CharPos, Mode := "Left") { ; Finds the next word break before or after the specified character position
		; or retrieves information about the character at that position.
		; CharPos : Character position.
		; Mode    : Can be one of the keys specified in 'WB'.
		; Returns the character index of the word break or other values depending on 'Mode'.
		; For details see http://msdn.microsoft.com/en-us/library/bb788018(v=vs.85).aspx.
		; EM_FINDWORDBREAK = 0x044C (WM_USER + 76)
		Static WB := {LEFT: 0, RIGHT: 1, ISDELIMITER: 2, CLASSIFY: 3, MOVEWORDLEFT: 4, MOVEWORDRIGHT: 5, LEFTBREAK: 6
                  , RIGHTBREAK: 7}
		Option := WB.HasKey(Mode) ? WB[Mode] : 0
		ErrorLevel := SendMessage(0x044C, Option, CharPos, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	GetSelText() { ; Retrieves the currently selected text as plain text.
		; Returns selected text.
		; EM_GETSELTEXT = 0x043E, EM_EXGETSEL = 0x0434
		;Old VarSetCapacity(CR, 8, 0)
		CR := Buffer(8, 0) ;Changed
		SendMessage(0x0434, 0, &CR, , "ahk_id " . This.HWND)
		L := NumGet(CR, 4, "Int") - NumGet(CR, 0, "Int") + 1
		If (L > 1) {
			;Old VarSetCapacity(Text, L * 2, 0)
			Text := Buffer(L * 2, 0) ;Changed
			SendMessage(0x043E, 0, &Text, , "ahk_id " . This.HWND)
			VarSetStrCapacity(&Text, -1)
		}
		Return Text
	}
	; -------------------------------------------------------------------------------------------------------------------
	GetSel() { ; Retrieves the starting and ending character positions of the selection in a rich edit control.
		; Returns an object containing the keys S (start of selection) and E (end of selection)).
		; EM_EXGETSEL = 0x0434
		;Old VarSetCapacity(CR, 8, 0)
		CR := Buffer(8, 0) ;Changed
		SendMessage(0x0434, 0, &CR, , "ahk_id " . This.HWND)
		Return {S: NumGet(CR, 0, "Int"), E: NumGet(CR, 4, "Int")}
	}
	; -------------------------------------------------------------------------------------------------------------------
	GetText() { ; Gets the whole content of the control as plain text.
		; EM_GETTEXTEX = 0x045E
		Text := ""
		If (Length := This.GetTextLen() * 2) {
			;Old VarSetCapacity(GTX, (4 * 4) + (A_PtrSize * 2), 0) ; GETTEXTEX structure
			GTX := Buffer((4 * 4) + (A_PtrSize * 2), 0) ; GETTEXTEX structure ; Changed
			NumPut(Length + 2, GTX, 0, "UInt") ; cb
			NumPut(1200, GTX, 8, "UInt")       ; codepage = Unicode
			;Old VarSetCapacity(Text, Length + 2, 0)
			Text := Buffer(Length + 2, 0) ; Changed
			SendMessage(0x045E, &GTX, &Text, , "ahk_id " . This.HWND)
			;Old VarSetCapacity(Text, -1)
			VarSetStrCapacity(&Text, -1) ;Changed
		}
		Return Text
	}
	; -------------------------------------------------------------------------------------------------------------------
	GetTextLen() { ; Calculates text length in various ways.
		; EM_GETTEXTLENGTHEX = 0x045F
		;OldVarSetCapacity(GTL, 8, 0)     ; GETTEXTLENGTHEX structure
		GTL := Buffer(8, 0)     ; GETTEXTLENGTHEX structure ; Changed
		NumPut(1200, GTL, 4, "UInt")  ; codepage = Unicode
		return SendMessage(0x045F, &GTL, 0, , "ahk_id " . This.HWND)
	}
	; -------------------------------------------------------------------------------------------------------------------
	GetTextRange(Min, Max) { ; Retrieves a specified range of characters from a rich edit control.
		; Min : Character position index immediately preceding the first character in the range.
		;       Integer value to store as cpMin in the CHARRANGE structure.
		; Max : Character position immediately following the last character in the range.
		;       Integer value to store as cpMax in the CHARRANGE structure.
		; CHARRANGE -> http://msdn.microsoft.com/en-us/library/bb787885(v=vs.85).aspx
		; EM_GETTEXTRANGE = 0x044B
		If (Max <= Min)
			Return ""
		;Old VarSetCapacity(Text, (Max - Min) << !!A_IsUnicode, 0)
		;Old VarSetCapacity(TEXTRANGE, 16, 0) ; TEXTRANGE Struktur
		;Old Text := Buffer((Max - Min) << !!A_IsUnicode, 0)
		Text := Buffer((Max - Min) << !!1, 0) ;Changed
		TEXTRANGE := Buffer(16, 0) ; TEXTRANGE Struktur
		NumPut(Min, TEXTRANGE, 0, "UInt")
		NumPut(Max, TEXTRANGE, 4, "UInt")
		NumPut(&Text, TEXTRANGE, 8, "UPtr")
		SendMessage(0x044B, 0, &TEXTRANGE, , "ahk_id " . This.HWND)
		VarSetStrCapacity(&Text, -1) ; Länge des Zeichenspeichers korrigieren 
		Return Text
	}
	; -------------------------------------------------------------------------------------------------------------------
	HideSelection(Mode) { ; Hides or shows the selection.
		; Mode : True to hide or False to show the selection.
		; EM_HIDESELECTION = 0x043F (WM_USER + 63)
		SendMessage(0x043F, Mode, 0, , "ahk_id " . This.HWND)
		Return True
	}
	; -------------------------------------------------------------------------------------------------------------------
	LimitText(Limit)  { ; Sets an upper limit to the amount of text the user can type or paste into a rich edit control.
		; Limit : Specifies the maximum amount of text that can be entered.
		;         If this parameter is zero, the default maximum is used, which is 64K characters.
		; EM_EXLIMITTEXT =  0x435 (WM_USER + 53)
		SendMessage(0x0435, 0, Limit, , "ahk_id " . This.HWND)
		Return True
	}
	; -------------------------------------------------------------------------------------------------------------------
	ReplaceSel(Text := "") { ; Replaces the selected text with the specified text.
		; EM_REPLACESEL = 0xC2
		ErrorLevel := SendMessage(0xC2, 1, &Text, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	SetText(&Text := "", Mode := "") { ; Replaces the selection or the whole content of the control.
		; Mode : Option flags. It can be any reasonable combination of the keys defined in 'ST'.
		; For details see http://msdn.microsoft.com/en-us/library/bb774284(v=vs.85).aspx.
		; EM_SETTEXTEX = 0x0461, CP_UNICODE = 1200
		; ST_DEFAULT = 0, ST_KEEPUNDO = 1, ST_SELECTION = 2, ST_NEWCHARS = 4 ???
		Static ST := {DEFAULT: 0, KEEPUNDO: 1, SELECTION: 2}
		Flags := 0
		Mode := StrSplit(Mode, " ")
		For Each, Value In Mode
			If ST.HasKey(Value)
				Flags |= ST[Value]
		CP := 1200
		BufAddr := &Text
		; RTF formatted text has to be passed as ANSI!!!
		If (SubStr(Text, 1, 5) = "{\rtf") || (SubStr(Text, 1, 5) = "{urtf") {
			Len := StrPut(Text, "CP0")
			;Old VarSetCapacity(Buf, Len, 0)
			Buf := Buffer(Len, 0) ;Changed
			StrPut(Text, &Buf, , "CP0")
			BufAddr := &Buf
			CP := 0
		}
		;Old VarSetCapacity(STX, 8, 0)     ; SETTEXTEX structure
		STX := Buffer(8, 0)     ; SETTEXTEX structure ;Changed
		NumPut(Flags, STX, 0, "UInt") ; flags
		NumPut(CP  ,  STX, 4, "UInt") ; codepage
		ErrorLevel := SendMessage(0x0461, &STX, BufAddr, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	SetSel(Start, End) { ; Selects a range of characters.
		; Start : zero-based start index
		; End   : zero-beased end index (-1 = end of text))
		; EM_EXSETSEL = 0x0437
		;Old VarSetCapacity(CR, 8, 0)
		CR := Buffer(8, 0) ;Changed
		NumPut(Start, CR, 0, "Int")
		NumPut(End,   CR, 4, "Int")
		return SendMessage(0x0437, 0, &CR, , "ahk_id " . This.HWND)
	}
	; -------------------------------------------------------------------------------------------------------------------
	; Appearance, styles, and options
	; -------------------------------------------------------------------------------------------------------------------
	AutoURL(On) { ; Turn AutoURLDetection on/off
		; EM_AUTOURLDETECT = 0x45B
		ErrorLevel := SendMessage(0x45B, !!On, 0, , "ahk_id " . This.HWND)
		WinRedraw("ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	GetOptions() { ; Retrieves rich edit control options.
		; Returns an array of currently the set options as the keys defined in 'ECO'.
		; For details see http://msdn.microsoft.com/en-us/library/bb774178(v=vs.85).aspx.
		; EM_GETOPTIONS = 0x044E
		Static ECO := {AUTOWORDSELECTION: 0x01, AUTOVSCROLL: 0x40, AUTOHSCROLL: 0x80, NOHIDESEL: 0x100
                   , READONLY: 0x800, WANTRETURN: 0x1000, SAVESEL: 0x8000, SELECTIONBAR: 0x01000000
                   , VERTICAL: 0x400000}
		O := SendMessage(0x044E, 0, 0, , "ahk_id " . This.HWND)
		Options := []
		For Key, Value In ECO
			If (O & Value)
				Options.Push(Key)
		Return Options
	}
	; -------------------------------------------------------------------------------------------------------------------
	GetStyles() { ; Retrieves the current edit style flags.
		; Returns an object containing keys as defined in 'SES'.
		; For details see http://msdn.microsoft.com/en-us/library/bb788031(v=vs.85).aspx.
		; EM_GETEDITSTYLE	= 0x04CD (WM_USER + 205)
		Static SES := {1: "EMULATESYSEDIT", 1: "BEEPONMAXTEXT", 4: "EXTENDBACKCOLOR", 32: "NOXLTSYMBOLRANGE", 64: "USEAIMM"
                   , 128: "NOIME", 256: "ALLOWBEEPS", 512: "UPPERCASE", 1024: "LOWERCASE", 2048: "NOINPUTSEQUENCECHK"
                   , 4096: "BIDI", 8192: "SCROLLONKILLFOCUS", 16384: "XLTCRCRLFTOCR", 32768: "DRAFTMODE"
                   , 0x0010000: "USECTF", 0x0020000: "HIDEGRIDLINES", 0x0040000: "USEATFONT", 0x0080000: "CUSTOMLOOK"
                   , 0x0100000: "LBSCROLLNOTIFY", 0x0200000: "CTFALLOWEMBED", 0x0400000: "CTFALLOWSMARTTAG"
                   , 0x0800000: "CTFALLOWPROOFING"}
		Result := SendMessage(0x04CD, 0, 0, , "ahk_id " . This.HWND)
		Styles := []
		For Key, Value In SES
			If (Result & Key)
				Styles.Push(Value)
		Return Styles
	}
	; -------------------------------------------------------------------------------------------------------------------
	GetZoom() { ; Gets the current zoom ratio.
		; Returns the zoom ratio in percent.
		; EM_GETZOOM = 0x04E0
		;Old VarSetCapacity(N, 4, 0), VarSetCapacity(D, 4, 0)
		N := Buffer(4, 0), D := Buffer(4, 0) ;Changed
		SendMessage(0x04E0, &N, &D, , "ahk_id " . This.HWND)
		N := NumGet(N, 0, "Int"), D := NumGet(D, 0, "Int")
		Return (N = 0) && (D = 0) ? 100 : Round(N / D * 100)
	}
	; -------------------------------------------------------------------------------------------------------------------
	SetBackgroundColor(Color) { ; Sets the background color.
		; Color : RGB integer value or HTML color name or
		;         "Auto" to reset to system default color.
		; Returns the prior background color.
		; EM_SETBKGNDCOLOR = 0x0443
		If (Color = "Auto")
			System := True, Color := 0
		Else
			System := False, Color := This.GetBGR(Color)
		r := SendMessage(0x0443, System, Color, , "ahk_id " . This.HWND)
		Return This.GetRGB(r)
	}
	; -------------------------------------------------------------------------------------------------------------------
	SetOptions(Options := "", Mode := "SET") { ; Sets the options for a rich edit control.
		; Options : Space delimited string of options as the keys defined in 'ECO'.
		; Mode    : Settings mode: SET, OR, AND, XOR
		; For details see http://msdn.microsoft.com/en-us/library/bb774254(v=vs.85).aspx.
		; EM_SETOPTIONS = 0x044D
		Static ECO := {AUTOWORDSELECTION: 0x01, AUTOVSCROLL: 0x40, AUTOHSCROLL: 0x80, NOHIDESEL: 0x100, READONLY: 0x800
                   , WANTRETURN: 0x1000, SAVESEL: 0x8000, SELECTIONBAR: 0x01000000, VERTICAL: 0x400000}
           , ECOOP := {SET: 0x01, OR: 0x02, AND: 0x03, XOR: 0x04}
		If !ECOOP.HasKey(Mode)
			Return False
		O := 0
		Options := StrSplit(Options, " ")
		For Each, Option In Options {
			Option := Trim(Option, " `t`n`r")
			If ECO.HasKey(Option)
				O |= ECO[Option]
			Else
				Return False
		}
		ErrorLevel := SendMessage(0x044D,  ECOOP[Mode], O, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	SetStyles(SetStyles:=0, ClearStyles:=0) { ; Sets the current edit style flags for a rich edit control.
		; SetStyles <= styles to set ; ClearStyles <= styles to clear.  Use space delimited strings for each
		; For details see http://msdn.microsoft.com/en-us/library/bb774236(v=vs.85).aspx.
		; EM_SETEDITSTYLE	= 0x04CC (WM_USER + 204)
		Static SES := {EMULATESYSEDIT: 1, BEEPONMAXTEXT: 2, EXTENDBACKCOLOR: 4, NOXLTSYMBOLRANGE: 32, USEAIMM: 64
                  , NOIME: 128, ALLOWBEEPS: 256, UPPERCASE: 512, LOWERCASE: 1024, NOINPUTSEQUENCECHK: 2048
                  , BIDI: 4096, SCROLLONKILLFOCUS: 8192, XLTCRCRLFTOCR: 16384, DRAFTMODE: 32768
                  , USECTF: 0x0010000, HIDEGRIDLINES: 0x0020000, USEATFONT: 0x0040000, CUSTOMLOOK: 0x0080000
                  , LBSCROLLNOTIFY: 0x0100000, CTFALLOWEMBED: 0x0200000, CTFALLOWSMARTTAG: 0x0400000
                  , CTFALLOWPROOFING: 0x0800000}
		Flags := Mask := 0
		
		if SetStyles is "Integer" 
			Flags := SetStyles
		else {
			SetStyles := StrSplit(SetStyles, " ")
			For _, v In SetStyles {
				v := Trim(v, " `t`n`r")
				If SES.HasKey(v) {
					Mask |= SES[v]
					Flags |= SES[v]
				}
			}
		}
		
		if ClearStyles is "Integer"
			Mask := Flags | ClearStyles
		else {
			ClearStyles := StrSplit(ClearStyles, " ")
			For _, v In ClearStyles {
				v := Trim(v, " `t`n`r")
				If SES.HasKey(v) {
					Mask |= SES[v]
				}
			}
		}
		
		If (Mask) {
			return SendMessage(0x04CC, Flags, Mask, , "ahk_id " This.HWND)
		}
		Return False
	}
	
	ClearStyles(clearStyles) {
		return this.SetStyles(, clearStyles)
	}
	; -------------------------------------------------------------------------------------------------------------------
	SetZoom(Ratio := "") { ; Sets the zoom ratio of a rich edit control.
		; Ratio : Float value between 100/64 and 6400; a ratio of 0 turns zooming off.
		; EM_SETZOOM = 0x4E1
		ErrorLevel := SendMessage(0x4E1, (Ratio > 0 ? Ratio : 100), 100, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	; Copy, paste, etc.
	; -------------------------------------------------------------------------------------------------------------------
	CanRedo() { ; Determines whether there are any actions in the control redo queue.
		; EM_CANREDO = 0x0455 (WM_USER + 85)
		ErrorLevel := SendMessage(0x0455, 0, 0, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	CanUndo() { ; Determines whether there are any actions in an edit control's undo queue.
		; EM_CANUNDO = 0x00C6
		ErrorLevel := SendMessage(0x00C6, 0, 0, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	Clear() {
		; WM_CLEAR = 0x303
		ErrorLevel := SendMessage(0x303, 0, 0, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	Copy() { ; WM_COPY = 0x301
		ErrorLevel := SendMessage(0x301, 0, 0, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	Cut() { ; WM_CUT = 0x300
		ErrorLevel := SendMessage(0x300, 0, 0, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	Paste() { ; WM_PASTE = 0x302
		ErrorLevel := SendMessage(0x302, 0, 0, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	Redo() { ; EM_REDO := 0x454
		ErrorLevel := SendMessage(0x454, 0, 0, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	Undo() { ; EM_UNDO = 0xC7
		ErrorLevel := SendMessage(0xC7, 0, 0, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	SelAll() { ; Select all
		Return This.SetSel(0, -1)
	}
	; -------------------------------------------------------------------------------------------------------------------
	Deselect() { ; Deselect all
		Sel := This.GetSel()
		Return This.SetSel(Sel.S, Sel.S)
	}
	; -------------------------------------------------------------------------------------------------------------------
	; Font & colors
	; -------------------------------------------------------------------------------------------------------------------
	ChangeFontSize(Diff) { ; Change font size
		; Diff : any positive or negative integer, positive values are treated as +1, negative as -1.
		; Returns new size.
		; EM_SETFONTSIZE = 0x04DF
		; Font size changes by 1 in the range 4 - 11 pt, by 2 for 12 - 28 pt, afterward to 36 pt, 48 pt, 72 pt, 80 pt,
		; and by 10 for > 80 pt. The maximum value is 160 pt, the minimum is 4 pt
		Font := This.GetFont()
		If (Diff > 0 && Font.Size < 160) || (Diff < 0 && Font.Size > 4)
			SendMessage(0x04DF,  (Diff > 0 ? 1 : -1), 0, , "ahk_id " . This.HWND)
		Else
			Return False
		Font := This.GetFont()
		Return Font.Size
	}
	; -------------------------------------------------------------------------------------------------------------------
	GetFont(Default := False) { ; Get current font
		; Set Default to True to get the default font.
		; Returns an object containing current options (see SetFont())
		; EM_GETCHARFORMAT = 0x043A
		; BOLD_FONTTYPE = 0x0100, ITALIC_FONTTYPE = 0x0200
		; CFM_BOLD = 1, CFM_ITALIC = 2, CFM_UNDERLINE = 4, CFM_STRIKEOUT = 8, CFM_PROTECTED = 16, CFM_SUBSCRIPT = 0x30000
		; CFM_BACKCOLOR = 0x04000000, CFM_CHARSET := 0x08000000, CFM_FACE = 0x20000000, CFM_COLOR = 0x40000000
		; CFM_SIZE = 0x80000000
		; CFE_SUBSCRIPT = 0x10000, CFE_SUPERSCRIPT = 0x20000, CFE_AUTOBACKCOLOR = 0x04000000, CFE_AUTOCOLOR = 0x40000000
		; SCF_SELECTION = 1
		Static Mask := 0xEC03001F
		Static Effects := 0xEC000000
		CF2 := New This.CF2
		CF2.Mask := Mask
		CF2.Effects := Effects
		SendMessage(0x043A, (Default ? 0 : 1), CF2.CF2, , "ahk_id " . This.HWND)
		Font := {}
		Font.Name := CF2.FaceName
		Font.Size := CF2.Height / 20
		CFS := CF2.Effects
		Style := (CFS & 1 ? "B" : "") . (CFS & 2 ? "I" : "") . (CFS & 4 ? "U" : "") . (CFS & 8 ? "S" : "")
             . (CFS & 0x10000 ? "L" : "") . (CFS & 0x20000 ? "H" : "") . (CFS & 16 ? "P" : "")
		Font.Style := Style = "" ? "N" : Style
		Font.Color := This.GetRGB(CF2.TextColor)
		If (CF2.Effects & 0x04000000)
			Font.BkColor := "Auto"
		Else
			Font.BkColor := This.GetRGB(CF2.BackColor)
		Font.CharSet := CF2.CharSet
		Return Font
	}
	; -------------------------------------------------------------------------------------------------------------------
	SetDefaultFont(Font := "") { ; Set default font
		; Font : Optional object - see SetFont().
		If IsObject(Font) {
			For Key, Value In Font
				If This.DefFont.HasKey(Key)
					This.DefFont[Key] := Value
		}
		Return This.SetFont(This.DefFont)
	}
	; -------------------------------------------------------------------------------------------------------------------
	SetFont(Font) { ; Set current/default font
		; Font : Object containing the following keys
		;        Name    : optional font name
		;        Size    : optional font size in points
		;        Style   : optional string of one or more of the following styles
		;                  B = bold, I = italic, U = underline, S = strikeout, L = subscript
		;                  H = superscript, P = protected, N = normal
		;        Color   : optional text color as RGB integer value or HTML color name
		;                  "Auto" for "automatic" (system's default) color
		;        BkColor : optional text background color (see Color)
		;                  "Auto" for "automatic" (system's default) background color
		;        CharSet : optional font character set
		;                  1 = DEFAULT_CHARSET, 2 = SYMBOL_CHARSET
		;        UlColor : 
		;        Empty parameters preserve the corresponding properties
		; EM_SETCHARFORMAT = 0x0444
		; SCF_DEFAULT = 0, SCF_SELECTION = 1
		CF2 := New This.CF2
		Mask := Effects := 0
		If (Font.Name != "") {
			Mask |= 0x20000000, Effects |= 0x20000000 ; CFM_FACE, CFE_FACE
			CF2.FaceName := Font.Name
		}
		Size := Font.Size
		If (Size != "") {
			If (Size < 161)
				Size *= 20
			Mask |= 0x80000000, Effects |= 0x80000000 ; CFM_SIZE, CFE_SIZE
			CF2.Height := Size
		}
		If (Font.Style != "") {
			Mask |= 0x3001F           ; all font styles
			If InStr(Font.Style, "B")
				Effects |= 1           ; CFE_BOLD
			If InStr(Font.Style, "I")
				Effects |= 2           ; CFE_ITALIC
			If InStr(Font.Style, "U")
				Effects |= 4           ; CFE_UNDERLINE
			If InStr(Font.Style, "S")
				Effects |= 8           ; CFE_STRIKEOUT
			If InStr(Font.Style, "P")
				Effects |= 16          ; CFE_PROTECTED
			If InStr(Font.Style, "L")
				Effects |= 0x10000     ; CFE_SUBSCRIPT
			If InStr(Font.Style, "H")
				Effects |= 0x20000     ; CFE_SUPERSCRIPT
		}
		If (Font.Color != "") {
			Mask |= 0x40000000        ; CFM_COLOR
			If (Font.Color = "Auto")
				Effects |= 0x40000000  ; CFE_AUTOCOLOR
			Else
				CF2.TextColor := This.GetBGR(Font.Color)
		}
		If (Font.BkColor != "") {
			Mask |= 0x04000000        ; CFM_BACKCOLOR
			If (Font.BkColor = "Auto")
				Effects |= 0x04000000  ; CFE_AUTOBACKCOLOR
			Else
				CF2.BackColor := This.GetBGR(Font.BkColor)
		}
		If (Font.CharSet != "") {
			Mask |= 0x08000000, Effects |= 0x08000000 ; CFM_CHARSET, CFE_CHARSET
			CF2.CharSet := Font.CharSet = 2 ? 2 : 1 ; SYMBOL|DEFAULT
		}
		If (Mask != 0) {
			Mode := Font.Default ? 0 : 1
			CF2.Mask := Mask
			CF2.Effects := Effects
			return SendMessage(0x0444, Mode, CF2.CF2, , "ahk_id " . This.HWND)
		}
		Return False
	}
	; -------------------------------------------------------------------------------------------------------------------
	ToggleFontStyle(Style) { ; Toggle single font style
		; Style : one of the following styles
		;         B = bold, I = italic, U = underline, S = strikeout, L = subscript, H = superschript, P = protected,
		;         N = normal (reset all other styles)
		; EM_GETCHARFORMAT = 0x043A, EM_SETCHARFORMAT = 0x0444
		; CFM_BOLD = 1, CFM_ITALIC = 2, CFM_UNDERLINE = 4, CFM_STRIKEOUT = 8, CFM_PROTECTED = 16, CFM_SUBSCRIPT = 0x30000
		; CFE_SUBSCRIPT = 0x10000, CFE_SUPERSCRIPT = 0x20000, SCF_SELECTION = 1
		CF2 :=This.GetCharFormat()
		CF2.Mask := 0x3001F ; FontStyles
		If (Style = "N")
			CF2.Effects := 0
		Else
			CF2.Effects ^= Style = "B" ? 1 : Style = "I" ? 2 : Style = "U" ? 4 : Style = "S" ? 8
                      : Style = "H" ? 0x20000 : Style = "L" ? 0x10000 : 0
		ErrorLevel := SendMessage(0x0444, 1, CF2.CF2, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	; Paragraph formatting
	; -------------------------------------------------------------------------------------------------------------------
	AlignText(Align := 1) { ; Set paragraph's alignment
		; Note: Values greater 3 doesn't seem to work though they should as documented
		; Align: may contain one of the following numbers:
		;        PFA_LEFT             1
		;        PFA_RIGHT            2
		;        PFA_CENTER           3
		;        PFA_JUSTIFY          4 // New paragraph-alignment option 2.0 (*)
		;        PFA_FULL_INTERWORD   4 // These are supported in 3.0 with advanced
		;        PFA_FULL_INTERLETTER 5 // typography enabled
		;        PFA_FULL_SCALED      6
		;        PFA_FULL_GLYPHS      7
		;        PFA_SNAP_GRID        8
		; EM_SETPARAFORMAT = 0x0447, PFM_ALIGNMENT = 0x08
		If (Align >= 1) && (ALign <= 8) {
			PF2 := New This.PF2    ; PARAFORMAT2 struct
			PF2.Mask := 0x08       ; dwMask
			PF2.Alignment := Align ; wAlignment
			SendMessage(0x0447, 0, PF2.PF2, , "ahk_id " . This.HWND)
			Return True
		}
		Return False
	}
	; -------------------------------------------------------------------------------------------------------------------
	SetBorder(Widths, Styles) { ; Set paragraph's borders
		; Borders are not displayed in RichEdit, so the call of this function has no visible result.
		; Even WordPad distributed with Win7 does not show them, but e.g. Word 2007 does.
		; Widths : Array of the 4 border widths in the range of 1 - 15 in order left, top, right, bottom; zero = no border
		; Styles : Array of the 4 border styles in the range of 0 - 7 in order left, top, right, bottom (see remarks)
		; Note:
		; The description on MSDN at http://msdn.microsoft.com/en-us/library/bb787942(v=vs.85).aspx is wrong!
		; To set borders you have to put the border width into the related nibble (4 Bits) of wBorderWidth
		; (in order: left (0 - 3), top (4 - 7), right (8 - 11), and bottom (12 - 15). The values are interpreted as
		; half points (i.e. 10 twips). Border styles are set in the related nibbles of wBorders.
		; Valid styles seem to be:
		;     0 : \brdrdash (dashes)
		;     1 : \brdrdashsm (small dashes)
		;     2 : \brdrdb (double line)
		;     3 : \brdrdot (dotted line)
		;     4 : \brdrhair (single/hair line)
		;     5 : \brdrs ? looks like 3
		;     6 : \brdrth ? looks like 3
		;     7 : \brdrtriple (triple line)
		; EM_SETPARAFORMAT = 0x0447, PFM_BORDER = 0x800
		If !IsObject(Widths)
			Return False
		W := S := 0
		For I, V In Widths {
			If (V)
				W |= V << ((A_Index - 1) * 4)
			If Styles[I]
				S |= Styles[I] << ((A_Index - 1) * 4)
		}
		PF2 := New This.PF2
		PF2.Mask := 0x800
		PF2.BorderWidth := W
		PF2.Borders := S
		ErrorLevel := SendMessage(0x0447, 0, PF2.PF2, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	SetLineSpacing(Lines) { ; Sets paragraph's line spacing.
		; Lines : number of lines as integer or float.
		; SpacingRule = 5:
		; The value of dyLineSpacing / 20 is the spacing, in lines, from one line to the next. Thus, setting
		; dyLineSpacing to 20 produces single-spaced text, 40 is double spaced, 60 is triple spaced, and so on.
		; EM_SETPARAFORMAT = 0x0447, PFM_LINESPACING = 0x100
		PF2 := New This.PF2
		PF2.Mask := 0x100
		PF2.LineSpacing := Abs(Lines) * 20
		PF2.LineSpacingRule := 5
		ErrorLevel := SendMessage(0x0447, 0, PF2.PF2, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	SetParaIndent(Indent := "Reset") { ; Sets space left/right of the paragraph.
		; Indent : Object containing up to three keys:
		;          - Start  : Optional - Absolute indentation of the paragraph's first line.
		;          - Right  : Optional - Indentation of the right side of the paragraph, relative to the right margin.
		;          - Offset : Optional - Indentation of the second and subsequent lines, relative to the indentation
		;                                of the first line.
		;          Values are interpreted as centimeters/inches depending on the user's locale measurement settings.
		;          Call without passing a parameter to reset indentation.
		; EM_SETPARAFORMAT = 0x0447
		; PFM_STARTINDENT  = 0x0001
		; PFM_RIGHTINDENT  = 0x0002
		; PFM_OFFSET       = 0x0004
		Static PFM := {STARTINDENT: 0x01, RIGHTINDENT: 0x02, OFFSET: 0x04}
		Measurement := This.GetMeasurement()
		PF2 := New This.PF2
		If (Indent = "Reset")
			PF2.Mask := 0x07 ; reset indentation
		Else If !IsObject(Indent)
			Return False
		Else {
			PF2.Mask := 0
			If (Indent.HasKey("Start")) {
				PF2.Mask |= PFM.STARTINDENT
				PF2.StartIndent := Round((Indent.Start / Measurement) * 1440)
			}
			If (Indent.HasKey("Offset")) {
				PF2.Mask |= PFM.OFFSET
				PF2.Offset := Round((Indent.Offset / Measurement) * 1440)
			}
			If (Indent.HasKey("Right")) {
				PF2.Mask |= PFM.RIGHTINDENT
				PF2.RightIndent := Round((Indent.Right / Measurement) * 1440)
			}
		}
		If (PF2.Mask) {
			ErrorLevel := SendMessage(0x0447, 0, PF2.PF2, , "ahk_id " . This.HWND)
			Return ErrorLevel
		}
		Return False
	}
	; -------------------------------------------------------------------------------------------------------------------
	SetParaNumbering(Numbering := "Reset") {
		; Numbering : Object containing up to four keys:
		;             - Type  : Options used for bulleted or numbered paragraphs.
		;             - Style : Optional - Numbering style used with numbered paragraphs.
		;             - Tab   : Optional - Minimum space between a paragraph number and the paragraph text.
		;             - Start : Optional - Sequence number used for numbered paragraphs (e.g. 3 for C or III)
		;             Tab is interpreted as centimeters/inches depending on the user's locale measurement settings.
		;             Call without passing a parameter to reset numbering.
		; EM_SETPARAFORMAT = 0x0447
		; PARAFORMAT numbering options
		; PFN_BULLET   1 ; tomListBullet
		; PFN_ARABIC   2 ; tomListNumberAsArabic:   0, 1, 2,	...
		; PFN_LCLETTER 3 ; tomListNumberAsLCLetter: a, b, c,	...
		; PFN_UCLETTER 4 ; tomListNumberAsUCLetter: A, B, C,	...
		; PFN_LCROMAN  5 ; tomListNumberAsLCRoman:  i, ii, iii,	...
		; PFN_UCROMAN  6 ; tomListNumberAsUCRoman:  I, II, III,	...
		; PARAFORMAT2 wNumberingStyle options
		; PFNS_PAREN     0x0000 ; default, e.g.,                 1)
		; PFNS_PARENS    0x0100 ; tomListParentheses/256, e.g., (1)
		; PFNS_PERIOD    0x0200 ; tomListPeriod/256, e.g.,       1.
		; PFNS_PLAIN     0x0300 ; tomListPlain/256, e.g.,        1
		; PFNS_NONUMBER  0x0400 ; used for continuation w/o number
		; PFNS_NEWNUMBER 0x8000 ; start new number with wNumberingStart
		; PFM_NUMBERING      0x0020
		; PFM_NUMBERINGSTYLE 0x2000
		; PFM_NUMBERINGTAB   0x4000
		; PFM_NUMBERINGSTART 0x8000
		Static PFM := {Type: 0x0020, Style: 0x2000, Tab: 0x4000, Start: 0x8000}
		Static PFN := {Bullet: 1, Arabic: 2, LCLetter: 3, UCLetter: 4, LCRoman: 5, UCRoman: 6}
		Static PFNS := {Paren: 0x0000, Parens: 0x0100, Period: 0x0200, Plain: 0x0300, None: 0x0400, New: 0x8000}
		PF2 := New This.PF2
		If (Numbering = "Reset")
			PF2.Mask := 0xE020
		Else If !IsObject(Numbering)
			Return False
		Else {
			If (Numbering.HasKey("Type")) {
				PF2.Mask |= PFM.Type
				PF2.Numbering := PFN[Numbering.Type]
			}
			If (Numbering.HasKey("Style")) {
				PF2.Mask |= PFM.Style
				PF2.NumberingStyle := PFNS[Numbering.Style]
			}
			If (Numbering.HasKey("Tab")) {
				PF2.Mask |= PFM.Tab
				PF2.NumberingTab := Round((Numbering.Tab / This.GetMeasurement()) * 1440)
			}
			If (Numbering.HasKey("Start")) {
				PF2.Mask |= PFM.Start
				PF2.NumberingStart := Numbering.Start
			}
		}
		If (PF2.Mask) {
			ErrorLevel := SendMessage(0x0447, 0, PF2.PF2, , "ahk_id " . This.HWND)
			Return ErrorLevel
		}
		Return False
	}
	; -------------------------------------------------------------------------------------------------------------------
	SetParaSpacing(Spacing := "Reset") { ; Set space before / after the paragraph
		; Spacing : Object containing one or two keys:
		;           - Before : additional space before the paragraph in points
		;           - After  : additional space after the paragraph in points
		;           Call without passing a parameter to reset spacing to zero.
		; EM_SETPARAFORMAT = 0x0447
		; PFM_SPACEBEFORE  = 0x0040
		; PFM_SPACEAFTER   = 0x0080
		Static PFM := {Before: 0x40, After: 0x80}
		PF2 := New This.PF2
		If (Spacing = "Reset")
			PF2.Mask := 0xC0 ; reset spacing
		Else If !IsObject(Spacing)
			Return False
		Else {
			If (Spacing.Before >= 0) {
				PF2.Mask |= PFM.Before
				PF2.SpaceBefore := Round(Spacing.Before * 20)
			}
			If (Spacing.After >= 0) {
				PF2.Mask |= PFM.After
				PF2.SpaceAfter := Round(Spacing.After * 20)
			}
		}
		If (PF2.Mask) {
			ErrorLevel := SendMessage(0x0447, 0, PF2.PF2, , "ahk_id " . This.HWND)
			Return ErrorLevel
		}
		Return False
	}
	; -------------------------------------------------------------------------------------------------------------------
	SetDefaultTabs(Distance) { ; Set default tabstops
		; Distance will be interpreted as inches or centimeters depending on the current user's locale.
		; EM_SETTABSTOPS = 0xCB
		Static DUI := 64      ; dialog units per inch
           , MinTab := 0.20 ; minimal tab distance
           , MaxTab := 3.00 ; maximal tab distance
		IM := This.GetMeasurement()
		Distance := StrReplace(Distance, "," , ".")
		Distance := Round(Distance / IM, 2)
		If (Distance < MinTab)
			Distance := MinTab
		If (Distance > MaxTab)
			Distance := MaxTab
		;Old VarSetCapacity(TabStops, 4, 0)
		TabStops := Buffer(4, 0) ;Changed
		NumPut(Round(DUI * Distance), TabStops, "Int")
		ErrorLevel := SendMessage(0xCB, 1, &TabStops, , "ahk_id " . This.HWND)
		Result := ErrorLevel
		DllCall("User32.dll\UpdateWindow", "Ptr", This.HWND)
		Return Result
	}
	; -------------------------------------------------------------------------------------------------------------------
	SetTabStops(TabStops := "Reset") { ; Set paragraph's tabstobs
		; TabStops is an object containing the integer position as hundredth of inches/centimeters as keys
		; and the alignment ("L", "C", "R", or "D") as values.
		; The position will be interpreted as hundredth of inches or centimeters depending on the current user's locale.
		; Call without passing a  parameter to reset to default tabs.
		; EM_SETPARAFORMAT = 0x0447, PFM_TABSTOPS = 0x10
		Static MinT := 30                ; minimal tabstop in hundredth of inches
		Static MaxT := 830               ; maximal tabstop in hundredth of inches
		Static Align := {L: 0x00000000   ; left aligned (default)
                     , C: 0x01000000   ; centered
                     , R: 0x02000000   ; right aligned
                     , D: 0x03000000}  ; decimal tabstop
		Static MAX_TAB_STOPS := 32
		IC := This.GetMeasurement()
		PF2 := New This.PF2
		PF2.Mask := 0x10
		If (TabStops = "Reset") {
			ErrorLevel := SendMessage(0x0447, 0, PF2.PF2, , "ahk_id " . This.HWND)
			Return !!(ErrorLevel)
		}
		If !IsObject(TabStops)
			Return False
		TabCount := 0
		Tabs  := []
		For Position, Alignment In TabStops {
			Position /= IC
			If (Position < MinT) Or (Position > MaxT)
         Or !Align.HasKey(Alignment) Or (A_Index > MAX_TAB_STOPS)
				Return False
			Tabs[A_Index] := (Align[Alignment] | Round((Position / 100) * 1440))
			TabCount := A_Index
		}
		If (TabCount) {
			PF2.TabCount := TabCount
			PF2.Tabs := Tabs
			ErrorLevel := SendMessage(0x0447, 0, PF2.PF2, , "ahk_id " . This.HWND)
			Return ErrorLevel
		}
		Return False
	}
	; -------------------------------------------------------------------------------------------------------------------
	; Line handling
	; -------------------------------------------------------------------------------------------------------------------
	GetLineCount() { ; Get the total number of lines
		
		; EM_GETLINECOUNT = 0xBA
		ErrorLevel := SendMessage(0xBA, 0, 0, , "ahk_id " . This.HWND)
		Return ErrorLevel
	}
	; -------------------------------------------------------------------------------------------------------------------
	GetCaretLine() { ; Get the line containing the caret
		; EM_LINEINDEX = 0xBB, EM_EXLINEFROMCHAR = 0x0436
		r := SendMessage(0xBB, -1, 0, , "ahk_id " . This.HWND)
		r := SendMessage(0x0436, 0, r, , "ahk_id " . This.HWND)
		Return r + 1
	}
	; -------------------------------------------------------------------------------------------------------------------
	; Statistics
	; -------------------------------------------------------------------------------------------------------------------
	GetStatistics() { ; Get some statistic values
		; Get the line containing the caret, it's position in this line, the total amount of lines, the absolute caret
		; position and the total amount of characters.
		; EM_GETSEL = 0xB0, EM_LINEFROMCHAR = 0xC9, EM_LINEINDEX = 0xBB, EM_GETLINECOUNT = 0xBA
		Stats := {}
		;Old VarSetCapacity(GTL, 8, 0)  ; GETTEXTLENGTHEX structure
		GTL := Buffer(8, 0)  ; GETTEXTLENGTHEX structure ;Changed
		SB := 0
		SendMessage(0xB0, &SB, 0, , "ahk_id " . This.HWND)
		SB := NumGet(SB, 0, "UInt") + 1
		r := SendMessage(0xBB, -1, 0, , "ahk_id " . This.HWND)
		Stats.LinePos := SB - r
		r := SendMessage(0xC9, -1, 0, , "ahk_id " . This.HWND)
		Stats.Line := r + 1
		r := SendMessage(0xBA, 0, 0, , "ahk_id " . This.HWND)
		Stats.LineCount := r
		Stats.CharCount := This.GetTextLen()
		Return Stats
	}
	; -------------------------------------------------------------------------------------------------------------------
	; Layout
	; -------------------------------------------------------------------------------------------------------------------
	WordWrap(On) { ; Turn wordwrapping on/off
		; EM_SCROLLCARET = 0xB7, EM_SETTARGETDEVICE = 0x0448
		Sel := This.GetSel()
		SendMessage(0x0448, 0, (On ? 0 : -1), , "ahk_id " . This.HWND)
		This.SetSel(Sel.S, Sel.E)
		SendMessage(0xB7, 0, 0,  "ahk_id " . This.HWND)
		Return On
	}
	; -------------------------------------------------------------------------------------------------------------------
	;~ WYSIWYG(On) { ; Show control as printed (WYSIWYG)
	;~ ; Text measuring is based on the default printer's capacities, thus changing the printer may produce different
	;~ ; results. See remarks/comments in Print() also.
	;~ ; EM_SCROLLCARET = 0xB7, EM_SETTARGETDEVICE = 0x0448
	;~ ; PD_RETURNDC = 0x0100, PD_RETURNDEFAULT = 0x0400
	;~ Static PDC := 0
	;~ Static PD_Size := (A_PtrSize = 4 ? 66 : 120)
	;~ Static OffFlags := A_PtrSize * 5
	;~ Sel := This.GetSel()
	;~ If !(On) {
	;~ DllCall("User32.dll\LockWindowUpdate", "Ptr", This.HWND)
	;~ DllCall("Gdi32.dll\DeleteDC", "Ptr", PDC)
	;~ SendMessage(0x0448, 0, -1, , "ahk_id " . This.HWND)
	;~ This.SetSel(Sel.S, Sel.E)
	;~ SendMessage(0xB7, 0, 0,  "ahk_id " . This.HWND)
	;~ DllCall("User32.dll\LockWindowUpdate", "Ptr", 0)
	;~ Return ErrorLevel
	;~ }
	;~ Numput(VarSetCapacity(PD, PD_Size, 0), PD)
	;~ NumPut(0x0100 | 0x0400, PD, A_PtrSize * 5, "UInt") ; PD_RETURNDC | PD_RETURNDEFAULT
	;~ If !DllCall("Comdlg32.dll\PrintDlg", "Ptr", &PD, "Int")
	;~ Return
	;~ DllCall("Kernel32.dll\GlobalFree", "Ptr", NumGet(PD, A_PtrSize * 2, "UPtr"))
	;~ DllCall("Kernel32.dll\GlobalFree", "Ptr", NumGet(PD, A_PtrSize * 3, "UPtr"))
	;~ PDC := NumGet(PD, A_PtrSize * 4, "UPtr")
	;~ DllCall("User32.dll\LockWindowUpdate", "Ptr", This.HWND)
	;~ Caps := This.GetPrinterCaps(PDC)
	;~ ; Set up page size and margins in pixel
	;~ UML := This.Margins.LT                   ; user margin left
	;~ UMR := This.Margins.RT                   ; user margin right
	;~ PML := Caps.POFX                         ; physical margin left
	;~ PMR := Caps.PHYW - Caps.HRES - Caps.POFX ; physical margin right
	;~ LPW := Caps.HRES                         ; logical page width
	;~ ; Adjust margins
	;~ UML := UML > PML ? (UML - PML) : 0
	;~ UMR := UMR > PMR ? (UMR - PMR) : 0
	;~ LineLen := LPW - UML - UMR
	;~ SendMessage(0x0448, PDC, LineLen, , "ahk_id " . This.HWND)
	;~ This.SetSel(Sel.S, Sel.E)
	;~ SendMessage(0xB7, 0, 0,  "ahk_id " . This.HWND)
	;~ DllCall("User32.dll\LockWindowUpdate", "Ptr", 0)
	;~ Return ErrorLevel
	;~ }
	; -------------------------------------------------------------------------------------------------------------------
	; File handling
	; -------------------------------------------------------------------------------------------------------------------
	;~ LoadFile(File, Mode := "Open") { ; Load file
	;~ ; File : file name
	;~ ; Mode : Open / Add / Insert
	;~ ;        Open   : Replace control's content
	;~ ;        Append : Append to conrol's content
	;~ ;        Insert : Insert at / replace current selection
	;~ If !FileExist(File)
	;~ Return False
	;~ SplitPath(File, , , Ext)
	;~ If (Ext = "rtf") {
	;~ If (Mode = "Open") {
	;~ Selection := False
	;~ } Else If (Mode = "Insert") {
	;~ Selection := True
	;~ } Else If (Mode = "Append") {
	;~ This.SetSel(-1, -2)
	;~ Selection := True
	;~ }
	;~ This.LoadRTF(File, Selection)
	;~ } Else {
	;~ Text := FileRead(File)
	;~ If (Mode = "Open") {
	;~ This.SetText(Text)
	;~ } Else If (Mode = "Insert") {
	;~ This.ReplaceSel(Text)
	;~ } Else If (Mode = "Append") {
	;~ This.SetSel(-1, -2)
	;~ This.ReplaceSel(Text)
	;~ }
	;~ }
	;~ Return True
	;~ }
	; -------------------------------------------------------------------------------------------------------------------
	;~ SaveFile(File) { ; Save file
	;~ ; File : file name
	;~ ; Returns True on success, otherwise False.
	
	;~ this.Gui.opt("+OwnDialogs")
	;~ SplitPath(File, , , Ext)
	;~ Text := Ext = "rtf" ? This.GetRTF() : This.GetText()
	;~ If IsObject(FileObj := FileOpen(File, "w")) {
	;~ FileObj.Write(Text)
	;~ FileObj.Close()
	;~ Return True
	;~ }
	;~ Return False
	;~ }
	; -------------------------------------------------------------------------------------------------------------------
	; Printing
	; THX jballi ->  http://www.autohotkey.com/board/topic/45513-function-he-print-wysiwyg-print-for-the-hiedit-control/
	; -------------------------------------------------------------------------------------------------------------------
	;~ Print() {
	;~ ; ----------------------------------------------------------------------------------------------------------------
	;~ ; Static variables
	;~ Static PD_ALLPAGES := 0x00, PD_SELECTION := 0x01, PD_PAGENUMS := 0x02, PD_NOSELECTION := 0x04
	;~ , PD_RETURNDC := 0x0100, PD_USEDEVMODECOPIES := 0x040000, PD_HIDEPRINTTOFILE := 0x100000
	;~ , PD_NONETWORKBUTTON := 0x200000, PD_NOCURRENTPAGE := 0x800000
	;~ , MM_TEXT := 0x1
	;~ , EM_FORMATRANGE := 0x0439, EM_SETTARGETDEVICE := 0x0448
	;~ , DocName := "AHKRichEdit"
	;~ , PD_Size := (A_PtrSize = 8 ? (13 * A_PtrSize) + 16 : 66)
	;~ ErrorMsg := ""
	;~ ; ----------------------------------------------------------------------------------------------------------------
	;~ ; Prepare to call PrintDlg
	;~ ; Define/Populate the PRINTDLG structure
	;~ VarSetCapacity(PD, PD_Size, 0)
	;~ Numput(PD_Size, PD, 0, "UInt")  ; lStructSize
	;~ Numput(This.Gui.hwnd, PD, A_PtrSize, "UPtr") ; hwndOwner
	;~ ; Collect Start/End select positions
	;~ Sel := This.GetSel()
	;~ ; Determine/Set Flags
	;~ Flags := PD_ALLPAGES | PD_RETURNDC | PD_USEDEVMODECOPIES | PD_HIDEPRINTTOFILE | PD_NONETWORKBUTTON
	;~ | PD_NOCURRENTPAGE
	;~ If (Sel.S = Sel.E)
	;~ Flags |= PD_NOSELECTION
	;~ Else
	;~ Flags |= PD_SELECTION
	;~ Offset := A_PtrSize * 5
	;~ NumPut(Flags, PD, Offset, "UInt")       ; Flags
	;~ ; Page and copies
	;~ NumPut( 1, PD, Offset += 4, "UShort")   ; nFromPage
	;~ NumPut( 1, PD, Offset += 2, "UShort")   ; nToPage
	;~ NumPut( 1, PD, Offset += 2, "UShort")   ; nMinPage
	;~ NumPut(-1, PD, Offset += 2, "UShort")   ; nMaxPage
	;~ NumPut( 1, PD, Offset += 2, "UShort")   ; nCopies
	;~ ; Note: Use -1 to specify the maximum page number (65535).
	;~ ; Programming note: The values that are loaded to these fields are critical. The Print dialog will not
	;~ ; display (returns an error) if unexpected values are loaded to one or more of these fields.
	;~ ; ----------------------------------------------------------------------------------------------------------------
	;~ ; Print dialog box
	;~ ; Open the Print dialog.  Bounce If the user cancels.
	;~ If !DllCall("Comdlg32.dll\PrintDlg", "Ptr", &PD, "UInt") {
	;~ ErrorLevel := "Function: " . A_ThisFunc . " - DLLCall of 'PrintDlg' failed."
	;~ Return False
	;~ }
	;~ ; Get the printer device context.  Bounce If not defined.
	;~ If !(PDC := NumGet(PD, A_PtrSize * 4, "UPtr")) { ; hDC
	;~ ErrorLevel := "Function: " . A_ThisFunc . " - Couldn't get a printer's device context."
	;~ Return False
	;~ }
	;~ ; Free global structures created by PrintDlg
	;~ DllCall("Kernel32.dll\GlobalFree", "Ptr", NumGet(PD, A_PtrSize * 2, "UPtr"))
	;~ DllCall("Kernel32.dll\GlobalFree", "Ptr", NumGet(PD, A_PtrSize * 3, "UPtr"))
	;~ ; ----------------------------------------------------------------------------------------------------------------
	;~ ; Prepare to print
	;~ ; Collect Flags
	;~ Offset := A_PtrSize * 5
	;~ Flags := NumGet(PD, OffSet, "UInt")           ; Flags
	;~ ; Determine From/To Page
	;~ If (Flags & PD_PAGENUMS) {
	;~ PageF := NumGet(PD, Offset += 4, "UShort") ; nFromPage (first page)
	;~ PageL := NumGet(PD, Offset += 2, "UShort") ; nToPage (last page)
	;~ } Else {
	;~ PageF := 1
	;~ PageL := 65535
	;~ }
	;~ ; Collect printer capacities
	;~ Caps := This.GetPrinterCaps(PDC)
	;~ ; Set up page size and margins in Twips (1/20 point or 1/1440 of an inch)
	;~ UML := This.Margins.LT                   ; user margin left
	;~ UMT := This.Margins.TT                   ; user margin top
	;~ UMR := This.Margins.RT                   ; user margin right
	;~ UMB := This.Margins.BT                   ; user margin bottom
	;~ PML := Caps.POFX                         ; physical margin left
	;~ PMT := Caps.POFY                         ; physical margin top
	;~ PMR := Caps.PHYW - Caps.HRES - Caps.POFX ; physical margin right
	;~ PMB := Caps.PHYH - Caps.VRES - Caps.POFY ; physical margin bottom
	;~ LPW := Caps.HRES                         ; logical page width
	;~ LPH := Caps.VRES                         ; logical page height
	;~ ; Adjust margins
	;~ UML := UML > PML ? (UML - PML) : 0
	;~ UMT := UMT > PMT ? (UMT - PMT) : 0
	;~ UMR := UMR > PMR ? (UMR - PMR) : 0
	;~ UMB := UMB > PMB ? (UMB - PMB) : 0
	;~ ; Define/Populate the FORMATRANGE structure
	;~ VarSetCapacity(FR, (A_PtrSize * 2) + (4 * 10), 0)
	;~ NumPut(PDC, FR, 0, "UPtr")         ; hdc
	;~ NumPut(PDC, FR, A_PtrSize, "UPtr") ; hdcTarget
	;~ ; Define FORMATRANGE.rc
	;~ ; rc is the area to render to (rcPage - margins), measured in twips (1/20 point or 1/1440 of an inch).
	;~ ; If the user-defined margins are smaller than the printer's margins (the unprintable areas at the edges of each
	;~ ; page), the user margins are set to the printer's margins. In addition, the user-defined margins must be adjusted
	;~ ; to account for the printer's margins.
	;~ ; For example: If the user requests a 3/4 inch (19.05 mm) left margin but the printer's left margin is 1/4 inch
	;~ ; (6.35 mm), rc.Left is set to 720 twips (1/2 inch or 12.7 mm).
	;~ Offset := A_PtrSize * 2
	;~ NumPut(UML, FR, Offset += 0, "Int")       ; rc.Left
	;~ NumPut(UMT, FR, Offset += 4, "Int")       ; rc.Top
	;~ NumPut(LPW - UMR, FR, Offset += 4, "Int") ; rc.Right
	;~ NumPut(LPH - UMB, FR, Offset += 4, "Int") ; rc.Bottom
	;~ ; Define FORMATRANGE.rcPage
	;~ ; rcPage is the entire area of a page on the rendering device, measured in twips (1/20 point or 1/1440 of an inch)
	;~ ; Note: rc defines the maximum printable area which does not include the printer's margins (the unprintable areas
	;~ ; at the edges of the page). The unprintable areas are represented by PHYSICALOFFSETX and PHYSICALOFFSETY.
	;~ NumPut(0, FR, Offset += 4, "Int")         ; rcPage.Left
	;~ NumPut(0, FR, Offset += 4, "Int")         ; rcPage.Top
	;~ NumPut(LPW, FR, Offset += 4, "Int")       ; rcPage.Right
	;~ NumPut(LPH, FR, Offset += 4, "Int")       ; rcPage.Bottom
	;~ ; Determine print range.
	;~ ; If "Selection" option is chosen, use selected text, otherwise use the entire document.
	;~ If (Flags & PD_SELECTION) {
	;~ PrintS := Sel.S
	;~ PrintE := Sel.E
	;~ } Else {
	;~ PrintS := 0
	;~ PrintE := -1            ; (-1 = Select All)
	;~ }
	;~ Numput(PrintS, FR, Offset += 4, "Int")    ; cr.cpMin
	;~ NumPut(PrintE, FR, Offset += 4, "Int")    ; cr.cpMax
	;~ ; Define/Populate the DOCINFO structure
	;~ VarSetCapacity(DI, A_PtrSize * 5, 0)
	;~ NumPut(A_PtrSize * 5, DI, 0, "UInt")
	;~ NumPut(&DocName, DI, A_PtrSize, "UPtr")     ; lpszDocName
	;~ NumPut(0       , DI, A_PtrSize * 2, "UPtr") ; lpszOutput
	;~ ; Programming note: All other DOCINFO fields intentionally left as null.
	;~ ; Determine MaxPrintIndex
	;~ If (Flags & PD_SELECTION) {
	;~ PrintM := Sel.E
	;~ } Else {
	;~ PrintM := This.GetTextLen()
	;~ }
	;~ ; Be sure that the printer device context is in text mode
	;~ DllCall("Gdi32.dll\SetMapMode", "Ptr", PDC, "Int", MM_TEXT)
	;~ ; ----------------------------------------------------------------------------------------------------------------
	;~ ; Print it!
	;~ ; Start a print job.  Bounce If there is a problem.
	;~ PrintJob := DllCall("Gdi32.dll\StartDoc", "Ptr", PDC, "Ptr", &DI, "Int")
	;~ If (PrintJob <= 0) {
	;~ ErrorLevel := "Function: " . A_ThisFunc . " - DLLCall of 'StartDoc' failed."
	;~ Return False
	;~ }
	;~ ; Print page loop
	;~ PageC  := 0 ; current page
	;~ PrintC := 0 ; current print index
	;~ While (PrintC < PrintM) {
	;~ PageC++
	;~ ; Are we done yet?
	;~ If (PageC > PageL)
	;~ Break
	;~ If (PageC >= PageF) && (PageC <= PageL) {
	;~ ; StartPage function.  Break If there is a problem.
	;~ If (DllCall("Gdi32.dll\StartPage", "Ptr", PDC, "Int") <= 0) {
	;~ ErrorMsg := "Function: " . A_ThisFunc . " - DLLCall of 'StartPage' failed."
	;~ Break
	;~ }
	;~ }
	;~ ; Format or measure page
	;~ If (PageC >= PageF) && (PageC <= PageL)
	;~ Render := True
	;~ Else
	;~ Render := False
	;~ SendMessage(EM_FORMATRANGE, Render, &FR, , "ahk_id " . This.HWND)
	;~ PrintC := ErrorLevel
	;~ If (PageC >= PageF) && (PageC <= PageL) {
	;~ ; EndPage function. Break If there is a problem.
	;~ If (DllCall("Gdi32.dll\EndPage", "Ptr", PDC, "Int") <= 0) {
	;~ ErrorMsg := "Function: " . A_ThisFunc . " - DLLCall of 'EndPage' failed."
	;~ Break
	;~ }
	;~ }
	;~ ; Update FR for the next page
	;~ Offset := (A_PtrSize * 2) + (4 * 8)
	;~ Numput(PrintC, FR, Offset += 0, "Int") ; cr.cpMin
	;~ NumPut(PrintE, FR, Offset += 4, "Int") ; cr.cpMax
	;~ }
	;~ ; ----------------------------------------------------------------------------------------------------------------
	;~ ; End the print job
	;~ DllCall("Gdi32.dll\EndDoc", "Ptr", PDC)
	;~ ; Delete the printer device context
	;~ DllCall("Gdi32.dll\DeleteDC", "Ptr", PDC)
	;~ ; Reset control (free cached information)
	;~ SendMessage %EM_FORMATRANGE%, 0, 0, , "ahk_id " . This.HWND
	;~ ; Return to sender
	;~ If (ErrorMsg) {
	;~ ErrorLevel := ErrorMsg
	;~ Return False
	;~ }
	;~ Return True
	;~ }
	; -------------------------------------------------------------------------------------------------------------------
	;~ GetMargins() { ; Get the default print margins
	;~ Static PSD_RETURNDEFAULT := 0x00000400, PSD_INTHOUSANDTHSOFINCHES := 0x00000004
	;~ , I := 1000 ; thousandth of inches
	;~ , M := 2540 ; hundredth of millimeters
	;~ , PSD_Size := (4 * 10) + (A_PtrSize * 11)
	;~ , PD_Size := (A_PtrSize = 8 ? (13 * A_PtrSize) + 16 : 66)
	;~ , OffFlags := 4 * A_PtrSize
	;~ , OffMargins := OffFlags + (4 * 7)
	;~ If !This.HasKey("Margins") {
	;~ VarSetCapacity(PSD, PSD_Size, 0) ; PAGESETUPDLG structure
	;~ NumPut(PSD_Size, PSD, 0, "UInt")
	;~ NumPut(PSD_RETURNDEFAULT, PSD, OffFlags, "UInt")
	;~ If !DllCall("Comdlg32.dll\PageSetupDlg", "Ptr", &PSD, "UInt")
	;~ Return false
	;~ DllCall("Kernel32.dll\GlobalFree", UInt, NumGet(PSD, 2 * A_PtrSize, "UPtr"))
	;~ DllCall("Kernel32.dll\GlobalFree", UInt, NumGet(PSD, 3 * A_PtrSize, "UPtr"))
	;~ Flags := NumGet(PSD, OffFlags, "UInt")
	;~ Metrics := (Flags & PSD_INTHOUSANDTHSOFINCHES) ? I : M
	;~ Offset := OffMargins
	;~ This.Margins := {}
	;~ This.Margins.L := NumGet(PSD, Offset += 0, "Int")           ; Left
	;~ This.Margins.T := NumGet(PSD, Offset += 4, "Int")           ; Top
	;~ This.Margins.R := NumGet(PSD, Offset += 4, "Int")           ; Right
	;~ This.Margins.B := NumGet(PSD, Offset += 4, "Int")           ; Bottom
	;~ This.Margins.LT := Round((This.Margins.L / Metrics) * 1440) ; Left in twips
	;~ This.Margins.TT := Round((This.Margins.T / Metrics) * 1440) ; Top in twips
	;~ This.Margins.RT := Round((This.Margins.R / Metrics) * 1440) ; Right in twips
	;~ This.Margins.BT := Round((This.Margins.B / Metrics) * 1440) ; Bottom in twips
	;~ }
	;~ Return True
	;~ }
	; -------------------------------------------------------------------------------------------------------------------
	;~ GetPrinterCaps(DC) { ; Get printer's capacities
	;~ Static HORZRES         := 0x08, VERTRES         := 0x0A
	;~ , LOGPIXELSX      := 0x58, LOGPIXELSY      := 0x5A
	;~ , PHYSICALWIDTH   := 0x6E, PHYSICALHEIGHT  := 0x6F
	;~ , PHYSICALOFFSETX := 0x70, PHYSICALOFFSETY := 0x71
	;~ , Caps := {}
	;~ ; Number of pixels per logical inch along the page width and height
	;~ LPXX := DllCall("Gdi32.dll\GetDeviceCaps", "Ptr", DC, "Int", LOGPIXELSX, "Int")
	;~ LPXY := DllCall("Gdi32.dll\GetDeviceCaps", "Ptr", DC, "Int", LOGPIXELSY, "Int")
	;~ ; The width and height of the physical page, in twips.
	;~ Caps.PHYW := Round((DllCall("Gdi32.dll\GetDeviceCaps", "Ptr", DC, "Int", PHYSICALWIDTH, "Int") / LPXX) * 1440)
	;~ Caps.PHYH := Round((DllCall("Gdi32.dll\GetDeviceCaps", "Ptr", DC, "Int", PHYSICALHEIGHT, "Int") / LPXY) * 1440)
	;~ ; The distance from the left/right edge (PHYSICALOFFSETX) and the top/bottom edge (PHYSICALOFFSETY) of the
	;~ ; physical page to the edge of the printable area, in twips.
	;~ Caps.POFX := Round((DllCall("Gdi32.dll\GetDeviceCaps", "Ptr", DC, "Int", PHYSICALOFFSETX, "Int") / LPXX) * 1440)
	;~ Caps.POFY := Round((DllCall("Gdi32.dll\GetDeviceCaps", "Ptr", DC, "Int", PHYSICALOFFSETY, "Int") / LPXY) * 1440)
	;~ ; Width and height of the printable area of the page, in twips.
	;~ Caps.HRES := Round((DllCall("Gdi32.dll\GetDeviceCaps", "Ptr", DC, "Int", HORZRES, "Int") / LPXX) * 1440)
	;~ Caps.VRES := Round((DllCall("Gdi32.dll\GetDeviceCaps", "Ptr", DC, "Int", VERTRES, "Int") / LPXY) * 1440)
	;~ Return Caps
	;~ }
}
;*****************For Ole Callback
;~ RE_GetDocObj(HRE) {
   ;~ ; Get the document object of the specified RichEdit control
   ;~ Static IID_ITextDocument := "{8CC497C0-A1DF-11CE-8098-00AA0047BE5D}"
   ;~ DocObj := 0
   ;~ If DllCall("SendMessage", "Ptr", HRE, "UInt", 0x043C, "Ptr", 0, "PtrP", IRichEditOle, "UInt") { ; EM_GETOLEINTERFACE
      ;~ DocObj := ComObject(9, ComObjQuery(IRichEditOle, IID_ITextDocument), 1) ; ITextDocument
      ;~ ObjRelease(IRichEditOle)
   ;~ }
   ;~ Return DocObj
;~ }

swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: ObjRawSet & Array V1 => V2

18 Sep 2021, 16:49

AHK_user wrote:
04 Sep 2021, 04:44
in the Class PF2, "." and ":" are used as keys, but I assume that "attr" and "addr" are more logical.
not logical in the slightest. those are v1 hacks for storing binary data inside objects. v2 has Buffers, so those hacks are no longer needed. and u can give it a nicer interface:

Code: Select all

#Requires AutoHotkey v2.0-beta.1

class PF2 extends Buffer
{
	static MAX_TAB_STOPS := 32

	static PFM_STARTINDENT := 0x00000001
	static PFM_RIGHTINDENT := 0x00000002
	static PFM_OFFSET := 0x00000004
	static PFM_ALIGNMENT := 0x00000008
	static PFM_TABSTOPS := 0x00000010
	static PFM_NUMBERING := 0x00000020
	static PFM_OFFSETINDENT := 0x80000000
	...

	static __New() {
		setsFlag(propName, offset, type, flag) {
			this.Prototype.DefineProp(propName, {
				get: NumGet.Bind(, offset, type),
				set: (this, value) => (
					this.EnableFlag(flag),
					NumPut(type, value, this, offset),
					value
				)
			})
		}

		setsFlag('wNumbering', 8, 'UShort', this.PFM_NUMBERING)
		setsFlag('dxStartIndent', 12, 'Int', this.PFM_STARTINDENT)
		setsFlag('dxRightIndent', 16, 'Int', this.PFM_RIGHTINDENT)
		setsFlag('dxOffset', 20, 'Int', this.PFM_OFFSET)
		setsFlag('wAlignment', 24, 'UShort', this.PFM_ALIGNMENT)
		setsFlag('cTabCount', 26, 'UShort', this.PFM_TABSTOPS)
		...
	}

	dwMask {
		get => NumGet(this, 4, 'UInt')
		set => (NumPut('UInt', value, this, 4), value)
	}

	rgxTabs[oneBasedIndex := unset] {
		get {
			getTab(oneBasedIndex) => NumGet(this, 28 + ((oneBasedIndex - 1) * 4), 'Int')

			if IsSet(oneBasedIndex) ; eg rgxTabs[7]
				return getTab(oneBasedIndex)

			; eg rgxTabs
			Tabs := []
			Loop PF2.MAX_TAB_STOPS
				Tabs.Push(getTab(A_Index))

			return Tabs
		}

		set {
			setTab(oneBasedIndex, tab) => NumPut('Int', tab, this, 28 + ((oneBasedIndex - 1) * 4))

			if IsSet(oneBasedIndex) ; eg rgxTabs[7] := 123
			{
				this.AddFlag(PF2.PFM_TABSTOPS)
				setTab(oneBasedIndex, value)
				return value
			}

			; eg rgxTabs := [123, 456, 789]
			Tabs := value

			if (Tabs.Length > PF2.MAX_TAB_STOPS)
				throw ValueError('array bigger than MAX_TAB_STOPS(32)', -1, Tabs.Length)

			for oneBasedIndex, tab in Tabs
				setTab(oneBasedIndex, tab)

			this.cTabCount := Tabs.Length

			return Tabs
		}
	}

	__New() {
		super.__New(188, 0) ; sizeof(PARAFORMAT2)
		NumPut('UInt', this.Size, this, 0) ; cbSize
	}

	EnableFlag(flag) => this.dwMask |= flag
	DisableFlag(flag) => this.dwMask &= ~flag
	ToggleFlag(flag) => this.dwMask ^= flag
}

the __Set function of the class returns the undefined var [tabs], I assume this is an error in the original code.
seems like a copypaste bug, it should either return value or assign it to Tabs
vmech
Posts: 353
Joined: 25 Aug 2019, 13:03

Re: ObjRawSet & Array V1 => V2

16 Feb 2022, 20:08

@swagfag Can you explain why you used this particular form of property methods

Code: Select all

static __New() {
	...
	set: (this, value) => (this.EnableFlag(flag), NumPut(type, value, this, offset), value)
	...
}
and

Code: Select all

dwMask {
	...
	set => (NumPut('UInt', value, this, 4), value)
}
instead a (common?) simpler and shorter forms as

Code: Select all

set: (this, value) => (this.EnableFlag(flag), NumPut(type, value, this, offset))
and

Code: Select all

set => NumPut('UInt', value, this, 4)
In other words, what's the purpose of using final ( ... , value) in a property Set func definition ?
Please post your script code inside [code] ... [/code] block. Thank you.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: ObjRawSet & Array V1 => V2

17 Feb 2022, 01:56

set is generally supposed to return the value that's being assigned, otherwise ull be breaking multiple assignment scenarios. in other words, your set returns whatever NumPut returns
vmech
Posts: 353
Joined: 25 Aug 2019, 13:03

Re: ObjRawSet & Array V1 => V2

17 Feb 2022, 05:35

Thank you, I understand. Small but important knowledge. :thumbup:
Please post your script code inside [code] ... [/code] block. Thank you.
lexikos
Posts: 9560
Joined: 30 Sep 2013, 04:07
Contact:

Re: ObjRawSet & Array V1 => V2

10 Apr 2022, 03:00

Property setters aren't required to return a value in v2. A property assignment always evaluates to the value which was assigned, to reduce code size and avoid unexpected inconsistency. (Even the built-in objects were inconsistent before that, as some returned incorrect values or nothing at all.)
The return value of set is ignored. For example, val := obj.Property := 42 always assigns val := 42 regardless of what the property does, unless it throws an exception or exits the thread.

Return to “Ask for Help (v2)”

Who is online

Users browsing this forum: Descolada, KnightAckbar, marypoppins_1, MulPen, ntepa, Smile_, windfancy3, wineguy and 49 guests