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

class HeaderCellInfo(object):
	__slots__=['rowNumber','columnNumber','rowSpan','colSpan','minRowNumber','maxRowNumber','minColumnNumber','maxColumnNumber','name','isRowHeader','isColumnHeader']
	def __init__(self,**kwargs):
		self.rowSpan=self.colSpan=1
		self.minColumnNumber=self.maxColumnNumber=self.minRowNumber=self.maxRowNumber=None
		for  name,value in kwargs.iteritems():
			setattr(self,name,value)

class HeaderCellTracker(object):

	def __init__(self):
		self.infosDict={}
		self.listByRow=[]
		self.listByColumn=[]

	def addHeaderCellInfo(self,**kwargs):
		info=HeaderCellInfo(**kwargs)
		key=(info.rowNumber,info.columnNumber)
		self.infosDict[key]=info
		self.listByRow.append(key)
		self.listByRow.sort(reverse=True)
		self.listByColumn.append(key)
		self.listByColumn.sort(key=lambda k: (k[1],k[0]),reverse=True)

	def removeHeaderCellInfo(self,info):
		key=(info.rowNumber,info.columnNumber)
		self.listByRow.remove(key)
		self.listByColumn.remove(key)
		del self.infosDict[key]

	def getHeaderCellInfoAt(self,rowNumber,columnNumber):
		return self.infosDict.get((rowNumber,columnNumber))

	def iterPossibleHeaderCellInfosFor(self,rowNumber,columnNumber,minRowNumber=None,maxRowNumber=None,minColumnNumber=None,maxColumnNumber=None,columnHeader=False):
		for key in self.listByRow: #(self.listByColumn if columnHeader else self.listByRow):
			info=self.infosDict[key]
			if (info.minColumnNumber and info.minColumnNumber>columnNumber) or (info.maxColumnNumber and info.maxColumnNumber<columnNumber) or (info.minRowNumber and info.minRowNumber>rowNumber) or (info.maxRowNumber and info.maxRowNumber<rowNumber):
				# Skipping this possible header as the requested coordinates are outside the header's allowed range
				continue
			if (minColumnNumber and minColumnNumber>info.columnNumber) or (maxColumnNumber and maxColumnNumber<info.columnNumber) or (minRowNumber and minRowNumber>info.rowNumber) or (maxRowNumber and maxRowNumber<info.rowNumber):
				# Skipping this possible header as its coordinates are outside the requested range
				continue
			if (columnHeader and not info.isColumnHeader) or (not columnHeader and not info.isRowHeader):
				# Skipping this possible header as it is the wrong type of header
				continue
			if columnHeader and info.columnNumber<=columnNumber:
				if info.rowNumber<=rowNumber<(info.rowNumber+info.rowSpan):
					# We never want to yield column headers when actually on column headers
					return
				elif (info.rowNumber+info.rowSpan)<=rowNumber:
					# Found a valid column header for these coordinates
					yield info
			if not columnHeader and info.rowNumber<=rowNumber:
				if info.columnNumber<=columnNumber<(info.columnNumber+info.colSpan):
					# We never want to yield row headers when actually on row headers
					return
				elif (info.columnNumber+info.colSpan)<=columnNumber:
					# Found a valid row header for these coordinates
					yield info