akelEdit.py 3.59 KB
#A part of NonVisual Desktop Access (NVDA)
#Copyright (C) 2006-2008 NVDA Contributors <http://www.nvda-project.org/>
#This file is covered by the GNU General Public License.
#See the file COPYING for more details.

import edit
import winUser
import winKernel
import ctypes
import watchdog

# Messages
AEM_GETINDEX          =(winUser.WM_USER + 2106)
AEM_CHARFROMPOS       =(winUser.WM_USER + 2151)
AEM_POSFROMCHAR       =(winUser.WM_USER + 2152)
AEM_INDEXTORICHOFFSET =(winUser.WM_USER + 2112)
AEM_RICHOFFSETTOINDEX =(winUser.WM_USER + 2113)
AEM_CONTROLVERSION        =(winUser.WM_USER + 2200)

#AEM_GETINDEX flags
AEGI_LASTCHAR         =2
AEGI_CARETCHAR             =5
AEGI_NEXTLINE              =24


#Structures

class AELINEDATA(ctypes.Structure):
	pass

AELINEDATA._fields_=[
	('next',ctypes.POINTER(AELINEDATA)),
	('prev',ctypes.POINTER(AELINEDATA)),
	('wpLine',ctypes.c_wchar),
	('nLineLen',ctypes.c_int),
	('nLineBreak',ctypes.c_int),
	('nLineWidth',ctypes.c_int),
	('nSelStart',ctypes.c_int),
	('nSelEnd',ctypes.c_int),
]

class AECHARINDEX(ctypes.Structure):
	_fields_=[
		('nLine',ctypes.c_int),
		('lpLine',AELINEDATA),
		('nCharInLine',ctypes.c_int),
	]


class AkelEditTextInfo(edit.EditTextInfo):

	def _getLineNumFromOffset(self,offset):
		ciChar=AECHARINDEX()
		processHandle=self.obj.processHandle
		internalCiChar=winKernel.virtualAllocEx(processHandle,None,ctypes.sizeof(ciChar),winKernel.MEM_COMMIT,winKernel.PAGE_READWRITE)
		try:
			watchdog.cancellableSendMessage(self.obj.windowHandle,AEM_RICHOFFSETTOINDEX,offset,internalCiChar)
			winKernel.readProcessMemory(processHandle,internalCiChar,ctypes.byref(ciChar),ctypes.sizeof(ciChar),None)
		finally:
			winKernel.virtualFreeEx(processHandle,internalCiChar,0,winKernel.MEM_RELEASE)
		return ciChar.nLine

	def _getStoryLength(self):
		ciChar=AECHARINDEX()
		processHandle=self.obj.processHandle
		internalCiChar=winKernel.virtualAllocEx(processHandle,None,ctypes.sizeof(ciChar),winKernel.MEM_COMMIT,winKernel.PAGE_READWRITE)
		try:
			watchdog.cancellableSendMessage(self.obj.windowHandle,AEM_GETINDEX,AEGI_LASTCHAR,internalCiChar)
			end=watchdog.cancellableSendMessage(self.obj.windowHandle,AEM_INDEXTORICHOFFSET,0,internalCiChar)
		finally:
			winKernel.virtualFreeEx(processHandle,internalCiChar,0,winKernel.MEM_RELEASE)
		return end+1

	def _getLineOffsets(self,offset):
		(start,end)=super(AkelEditTextInfo,self)._getLineOffsets(offset)
		if end == self._getStoryLength():
			return (start,end)
		ciChar=AECHARINDEX()
		processHandle=self.obj.processHandle
		internalCiChar=winKernel.virtualAllocEx(processHandle,None,ctypes.sizeof(ciChar),winKernel.MEM_COMMIT,winKernel.PAGE_READWRITE)
		try:
			watchdog.cancellableSendMessage(self.obj.windowHandle,AEM_RICHOFFSETTOINDEX,offset,internalCiChar)
			watchdog.cancellableSendMessage(self.obj.windowHandle,AEM_GETINDEX,AEGI_NEXTLINE,internalCiChar)
			end=watchdog.cancellableSendMessage(self.obj.windowHandle,AEM_INDEXTORICHOFFSET,0,internalCiChar)
		finally:
			winKernel.virtualFreeEx(processHandle,internalCiChar,0,winKernel.MEM_RELEASE)
		return (start,end)


class AkelEdit(edit.RichEdit20):

	TextInfo=AkelEditTextInfo

	def initOverlayClass(self):
		global AEGI_NEXTLINE
		version=self._getControlVersion()
		if version <1.6:
			AEGI_NEXTLINE =8
		else:
			AEGI_NEXTLINE =24

		for gesture in ("kb:control+upArrow", "kb:control+downArrow"):
			self.bindGesture(gesture, "caret_moveByLine")

	def _getControlVersion(self):
		res=watchdog.cancellableSendMessage(self.windowHandle,AEM_CONTROLVERSION,None,None)
		major=winUser.LOBYTE(winUser.LOWORD(res))
		minor=winUser.HIBYTE(winUser.LOWORD(res))
		version=major+(0.1*minor)
		return version