Repository: nvaccess/nvda

Issue:
Commands to facilitate navigation on the tables
**Reported by EltonLopes on 2010-10-02 18:00**
In a table, would like the following commands to facilitate navigation on the tables and also out of them:

Control + Alt + Home: Go to the first table cell
Control + Alt + End: Go to the last cell of the table
Control + Alt + Page Up: Jump Up cell x in the same column
Control + Alt + Page Down: Jumps x cells down in the same column



Discussion:
Comment 1 by jteh on 2010-10-03 23:13
Changes:
Milestone changed from None to None

Comment 2 by jhomme on 2010-10-04 11:09
And one to jump to the first cell in the current row, and another to jump to the last cell in the current row please. Thanks.

Comment 3 by EltonLopes (in reply to comment 2) on 2010-10-04 13:10
Possibly, the following keys:
Control+alt+shift+Home: jump to the first cell in the current row
Control+alt+shift+end: jump to the last cell in the current row
or then, I suggest the keys:
alt+shift+Home: jump to the first cell in the current row
alt+shift+end: jump to the last cell in the current row

Replying to [jhomme](comment:2):

> And one to jump to the first cell in the current row, and another to jump to the last cell in the current row please. Thanks.

Comment 4 by orcauser on 2010-10-06 08:33
To add to the wish list:
a shortcut key to present (braille flash message/speech) the current cell coordinates.
Thank you.

Awesome stuff!
I would change a few things though.

> Control + Alt + Home: Go to the first table cell

This can be done with Shift+comma.

> Control + Alt + End: Go to the last cell of the table

This can be done with comma, then upArrow.

> And one to jump to the first cell in the current row, and another to jump to the last cell in the current row please.

If we free up Ctrl+Alt+{home,end} as suggested above, then those keys would become available to go to the first/last cell of the current row. If not, then I'd propose a command without the Shift, beause that looks like you are going to select cells.
I wonder if we want something like this for the first/last cell in the column?

> a shortcut key to present (braille flash message/speech) the current cell coordinates.

This is already spoken and brailled for every cell.
> > Control + Alt + Home: Go to the first table cell

> This can be done with Shift+comma.

Only in browse mode though

> > Control + Alt + End: Go to the last cell of the table

> This can be done with comma, then upArrow.

Only in browse mode

I propose:

* ctrl+shift+alt+left arrow: go to the first cell in the current row
* ctrl+shift+alt+right arrow: go to the last cell in the current row
* ctrl+shift+alt+up arrow: go to the first cell in the current column
* ctrl+shift+alt+down arrow: go to the last cell in the current column

ctrl+alt+home/end would be the most obvious hot keys for me to jump to begin/end of tables
Barring my comment about the Shift key this makes a lot of sense. And the use of the Shift key is only a minor concern. I like it!
Looks good. I don't think this is on our current road map, but we are happy to accept a pull request for this.
Just realised, its not clear which application this is talking about. Would the expectation be a general solution that works in all tables (Office applications, browsers, etc?)
Definitely browse mode. But for consistency we should probably do Office too. I'm not sure if that is technically easy to do though.
I would recommend against usage of 4-key combinations because a
notable amount of laptop keyboards do not support more than three keys
at once. In fact, this was the case when I was slightly frustrated
with the non-functional nature of the NVDA+Ctrl+Shift+arrow keys to
interact with the Synth Settings Ring on a Netbook I owned in the
past. I am concerned that increasing use of 4-key combinations may
further isolate users of such laptop keyboards from newer features.
Keeping in mind this limitation of the current proposal, I would like
to suggest the following alternative:
Ctrl+Alt+Home/End should move to the first or last cell in the current
row respectively
Ctrl+Alt+PgUP/PgDn should move to the first or last cell in the
current column respectively
Pressing both shortcuts would effectively achieve the function of
moving to the start or end of the table as well.
Thanks.

On 6/12/17, Davy Kager <notifications@github.com> wrote:
> Definitely browse mode. But for consistency we should probably do Office
> too. I'm not sure if that is technically easy to do though.
>
> --
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly or view it on GitHub:
> https://github.com/nvaccess/nvda/issues/957#issuecomment-307760377


-- 
Best Regards
Bhavya Shah

Blogger at Hiking Across Horizons: https://bhavyashah125.wordpress.com/

Contacting Me
E-mail Address: bhavya.shah125@gmail.com
Follow me on Twitter @BhavyaShah125 or www.twitter.com/BhavyaShah125
Mobile Number: +91 7506221750

I think it is meant to work in every situation where currently control+alt+arrows work, e.g. in browse mode, but also in multi column list views.

@bhavyashah: Not sure what to think about those 4 key combination problems. What I like about your combinations is that they leave out the shift key as also preferred by @dkager in https://github.com/nvaccess/nvda/issues/957#issuecomment-307380293

How about ctrl+alt+backspace for start of table and ctrl+alt+enter for end of table?
How about nvda+del for row+col?

On Fri, Jun 9, 2017 at 6:52 AM, Davy Kager <notifications@github.com> wrote:

> Awesome stuff!
> I would change a few things though.
>
> Control + Alt + Home: Go to the first table cell
>
> This can be done with Shift+comma.
>
> Control + Alt + End: Go to the last cell of the table
>
> This can be done with comma, then upArrow.
>
> And one to jump to the first cell in the current row, and another to jump
> to the last cell in the current row please.
>
> If we free up Ctrl+Alt+{home,end} as suggested above, then those keys
> would become available to go to the first/last cell of the current row. If
> not, then I'd propose a command without the Shift, beause that looks like
> you are going to select cells.
> I wonder if we want something like this for the first/last cell in the
> column?
>
> a shortcut key to present (braille flash message/speech) the current cell
> coordinates.
>
> This is already spoken and brailled for every cell.
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/nvaccess/nvda/issues/957#issuecomment-307380293>, or mute
> the thread
> <https://github.com/notifications/unsubscribe-auth/AFGivWyISbPE55IOTSVixqDDmS0MEQZ7ks5sCUAFgaJpZM4N1T_A>
> .
>



-- 

Derek Riemer: Improving the world one byte at a time!

   - University of Colorado Boulder Department of computer science, 4th
   year undergraduate student.
   - Accessibility enthusiast.
   - Proud user of the NVDA screen reader.
   - Open source enthusiast.
   - Skier.

Personal website <http://derekriemer.com>

How about control+alt+home, and then up or left arrow to fly to the
beginning of either column or row?

On Mon, Jun 12, 2017 at 8:31 AM, Leonard de Ruijter <
notifications@github.com> wrote:

> I think it is meant to work in every situation where currently
> control+alt+arrows work, e.g. in browse mode, but also in multi column list
> views.
>
> @bhavyashah <https://github.com/bhavyashah>: Not sure what to think about
> those 4 key combination problems. What I like about your combinations is
> that they leave out the shift key as also preferred by @dkager
> <https://github.com/dkager> in #957 (comment)
> <https://github.com/nvaccess/nvda/issues/957#issuecomment-307380293>
>
> How about ctrl+alt+backspace for start of table and ctrl+alt+enter for end
> of table?
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/nvaccess/nvda/issues/957#issuecomment-307807065>, or mute
> the thread
> <https://github.com/notifications/unsubscribe-auth/AFGivYwI4JUdVARuTokUexxfYKorsLDDks5sDUvHgaJpZM4N1T_A>
> .
>



-- 

Derek Riemer: Improving the world one byte at a time!

   - University of Colorado Boulder Department of computer science, 4th
   year undergraduate student.
   - Accessibility enthusiast.
   - Proud user of the NVDA screen reader.
   - Open source enthusiast.
   - Skier.

Personal website <http://derekriemer.com>

[**@leonardder**](https://github.com/leonardder) commented on [13 Jun 2017, 00:31 GMT+10](https://github.com/nvaccess/nvda/issues/957#issuecomment-307807065 "2017-06-12T14:31:31Z - Replied by Github Reply Comments"):
> How about ctrl+alt+backspace for start of table and ctrl+alt+enter for end of table?

This is eating a lot of key combinations already and key combinations are somewhat limited. I'd be inclined to say we should just leave out the start and end of table commands. It's really not that hard to hit jump to top of column then jump to start of row, etc. Alternatively, they could be implemented but unbound by default.

It's worth noting that outside of browse mode, you're going to have to implement these commands separately for every implementation. For Word, we'll probably eventually roll the table navigation commands into browse mode, since we have browse mode in Word now. That still leaves list views, though.
Since one can already use the browse mode commands to jump to the
start or end of a table, I am imagining that there wouldn't be
relatively sufficient cases where quick table interactions would be
necessary strictly in focus mode. I would stick to my earlier proposal
of Ctrl+Alt+Home/End/PgUP/PgDn because they seem comprehensive to me,
relatable and 3-key shortcuts. I agree with @jcteh about the
overutilization of keystrokes given that I am of the view that
start/end of the table can be achieved by my proposal by two commands
instead of one plus my belief that cases of strictly focus mode
interaction of tables would be somewhat limited.
I do not use NVDA+Del to any extent, but I reckon it sounds fitting
for said function.

On 6/13/17, James Teh <notifications@github.com> wrote:
> [**@leonardder**](https://github.com/leonardder) commented on [13 Jun 2017,
> 00:31
> GMT+10](https://github.com/nvaccess/nvda/issues/957#issuecomment-307807065
> "2017-06-12T14:31:31Z - Replied by Github Reply Comments"):
>> How about ctrl+alt+backspace for start of table and ctrl+alt+enter for end
>> of table?
>
> This is eating a lot of key combinations already and key combinations are
> somewhat limited. I'd be inclined to say we should just leave out the start
> and end of table commands. It's really not that hard to hit jump to top of
> column then jump to start of row, etc. Alternatively, they could be
> implemented but unbound by default.
>
> It's worth noting that outside of browse mode, you're going to have to
> implement these commands separately for every implementation. For Word,
> we'll probably eventually roll the table navigation commands into browse
> mode, since we have browse mode in Word now. That still leaves list views,
> though.
>
> --
> You are receiving this because you were mentioned.
> Reply to this email directly or view it on GitHub:
> https://github.com/nvaccess/nvda/issues/957#issuecomment-307962242


-- 
Best Regards
Bhavya Shah

Blogger at Hiking Across Horizons: https://bhavyashah125.wordpress.com/

Contacting Me
E-mail Address: bhavya.shah125@gmail.com
Follow me on Twitter @BhavyaShah125 or www.twitter.com/BhavyaShah125
Mobile Number: +91 7506221750

I'd be interested in an example of such a list view.
@feerrenrut could you update the issue title to be more specific?
I think we agreed on:
- Commands to navigate to the first/last column/row.
- Unbound commands to go to the start/end of a table. This basically combines going to the first/last row *and* column.

#### Technical

For first/last row/column in browse mode I suggest extending _getNearestTableCell to also accept "first" and "last" as movement directions. The function should probably be renamed to _getTableCell or _getTableCellInDirection.

This requires that back-ends can easily find the last row/column. The first row/column isn't a problem, assuming it always has index 0.
### Summary ###
* We all agree that – (i) navigating to the first/last cell of the current row/column/table are useful for a screen reader user, (ii) overutilization of key combinations should be avoided, (iii) 3-key commands are more preferable then 4-key commands.
* There is some debate on whether navigation to the first and last cell of the table merits separate key commands. Some argue that these would be useful, while others argue that moving to the first cell of the current row and then current column and similarly for the last cell of the current row and column in effect achieve this very end goal of moving to the first and last cell of the table. Keeping in mind the fact that increasingly limited key combinations are available, the best reconciliation of accommodating both sides is by introducing the ability to navigate to the first and last cell of the table as assignments that are unbound by default.
* No consensus as such was finally reached on the most intuitive set of keystrokes, but below is what appeared unrefuted and largely agreed upon:
Moves to the first cell of the current row – Ctrl+Alt+Home
Moves to the last cell of the current row – Ctrl+Alt+End
Moves to the first cell of the current column – Ctrl+Alt+PgUp
Moves to the last cell of the current column – Ctrl+Alt+PgDn
Moves to the first cell of the table – unassigned
Moves to the last cell of the table – unassigned
* The expected scope of implementation of these table navigation shortcuts would be global – all applications, both browse mode and focus mode, list views, etc. However, any technical limitations to this expectation of an ideal implementation will need to be taken into consideration.

Hi, I have a prototype implementation for moving to beginning and end of a row (adopting yet another feature from one of my add-ons). However, as summarized above, some follow-up discussion should take place. For anyone working on this, take a look at NVDAObjects.behaviors.RowWithFakeNavigation class. Thanks.
A question related to this issue has been asked in an NVDA's spanish mailing list. We will be grateful if you can check this again or if you could accept a pull request. My suggestion is to add the following commands:
- control+alt+home: move to the first cell of the current row.
- control+alt+end: move to the last cell of the current row.
- control+alt+repag: move to the first cell of the current column
- control+alt+avpag: move to the last cell of the current column.
- control+alt+numbers: move to columns from 1 to 10 respect to the current one, considering that 1 is the current column.
We should consider that control+alt can be used to shortcuts, so, as an alternative, NVDA+control+numbers could be more comfortable.
@ABuffEr  and @mltony worked on reviewing columns and navigation in tables, so I mention them here in case they are interested. I can add commands in source/NVDAObjects/behaviors.py, class RowWithFakeNavigation, if you find helpful a PR from me.
For reference, this has been asked at
https://nvdaes.groups.io/g/lista/message/987

I would love this feature, especially at work it would help alot. ctrl+alt however is the better option in my opinion because it is very related to ctrl+alt+arrow keys and it is also easy to keep in mind for beginners.
I implemented this feature in Tony's enhancements add-on:
https://github.com/mltony/nvda-tonys-enhancements
It can be easily integrated into NVDA if someone wants to work on that PR.
This issue is very old. What is blocking?

I would also love have this feature integrated in core. Would be a great gain of productivity when exploring big tables on webpages. Of course, meanwhile @mltony's add-on does the job. Thanks Tony!

Note that this feature is in Jaws for at least 10 years. And on the contrary to other recent Jaws features (OCR, ResearchIt), it is a real screen-reader feature.

If there is too much discussion on this issue, I would suggest making a minimal PR with the parts of this feature on which all agree, i.e.:

* implement only the 4 commands go to start of column, end of column, start of row and end of row
* do not assign default shortcuts

Then additional PRs could be merged if and when an agreement is found:

* assign default shortcut
* scripts to go to first/last cell of the table
* scripts to move to column 1, 2, 3, etc. 

The goal to proceed this way would be to avoid waiting another 10 yers.
Thanks.


I agree with Cyrille in moving forward with this. I proposed a way to implement this. If you agree, I can make a PR or at least a draft.
Just to clarify that my proposed implementation is quite incomplete. I have performed a git grep -n to search files containing alt+control+arrow gestures and this require changes in documentbase and files related to Microsoft Word. Also, the number of rows and columns of tables seems not to be explicitly retrieved in documentBase.py. I have looked at TonysEnhancements add-on, very quicly, and I find a value of 500, not the exact number of rows (I maybe wrong, since I have seen this very fast). So, this will require some more staff respect my first thinking.

@feerrenrut Could you please update this ticket with your comments when you are able? This ticket is over a decade old now and while there have been differences on what keystrokes to assign, there seems to have been agreement over the years on implementing unbound gestures for moving to the first/last cell of a row/column. In particular, it might be helpful if you could outline what implementation of this feature, if any, you are willing to accept a pull request for. May I also tag all the code contributors who at some point expressed interest in this functionality: @dkager  @leonardder  @derekriemer  @josephsl  @nvdaes  @mltony 
@bhavyashah The path forward suggested by @CyrilleB79  (https://github.com/nvaccess/nvda/issues/957#issuecomment-555884590) seems appropriate. As for implementation, this isn't something NV Access is currently looking at. If someone starts to work on this, I'd recommend starting with a prototype (draft PR) and get help with manual testing to look for issues.
Do we have to wait another 10 years?
Would love to see nvda improve on this.


Resolving patch:
diff --git a/source/documentBase.py b/source/documentBase.py
--- a/source/documentBase.py
+++ b/source/documentBase.py
@@ -11,7 +11,6 @@
 import textInfos
 import controlTypes
 
-
 _TableID = Union[int, Tuple, Any]
 """
 A variety of types can be used for a tableID.
@@ -27,6 +26,8 @@ class _Axis(str, Enum):
 class _Movement(str, Enum):
 	NEXT = "next"
 	PREVIOUS = "previous"
+	FIRST = "first"
+	LAST = "last"
 
 
 @dataclass
@@ -75,6 +76,30 @@ class DocumentWithTableNavigation(TextContainerObject,ScriptableObject):
 
 	_lastTableSelection: Optional[_TableSelection] = None
 
+	def _maybeGetLayoutTableIds(self, info: textInfos.TextInfo):
+		"""
+		If "Include layout tables" option is on, this will
+		compute the set of layout tables that this textInfo is enclosed in,
+		otherwise it will return empty set.
+		@param info:  the position where the layout tables should be looked for.
+		@returns: A set of table IDs or empty set.
+		"""
+		fields = list(info.getTextWithFields())
+		# If layout tables should not be reported, we should First record the ID of all layout tables,
+		# so that we can skip them when searching for the deepest table
+		layoutIDs = set()
+		if not config.conf["documentFormatting"]["includeLayoutTables"]:
+			for field in fields:
+				if (
+					isinstance(field, textInfos.FieldCommand)
+					and field.command == "controlStart"
+					and field.field.get('table-layout')
+				):
+					tableID = field.field.get('table-id')
+					if tableID is not None:
+						layoutIDs.add(tableID)
+		return layoutIDs
+
 	def _getTableCellCoords(self, info):
 		"""
 		Fetches information about the deepest table cell at the given position.
@@ -88,14 +113,7 @@ def _getTableCellCoords(self, info):
 			info = info.copy()
 			info.expand(textInfos.UNIT_CHARACTER)
 		fields=list(info.getTextWithFields())
-		# If layout tables should not be reported, we should First record the ID of all layout tables so that we can skip them when searching for the deepest table
-		layoutIDs=set()
-		if not config.conf["documentFormatting"]["includeLayoutTables"]:
-			for field in fields:
-				if isinstance(field, textInfos.FieldCommand) and field.command == "controlStart" and field.field.get('table-layout'):
-					tableID=field.field.get('table-id')
-					if tableID is not None:
-						layoutIDs.add(tableID)
+		layoutIDs = self._maybeGetLayoutTableIds(info)
 		for field in reversed(fields):
 			if not (isinstance(field, textInfos.FieldCommand) and field.command == "controlStart"):
 				# Not a control field.
@@ -112,6 +130,40 @@ def _getTableCellCoords(self, info):
 			attrs["table-rownumber"], attrs["table-columnnumber"],
 			attrs.get("table-rowsspanned", 1), attrs.get("table-columnsspanned", 1))
 
+	def _getTableDimensions(self, info: textInfos.TextInfo) -> Tuple[int, int]:
+		"""
+		Fetches information about the deepest table dimension.
+		@param info:  the position where the table cell should be looked for.
+		@returns: a tuple of table height and width.
+		@raises: LookupError if there is no table cell at this position.
+		"""
+		if info.isCollapsed:
+			info = info.copy()
+			info.expand(textInfos.UNIT_CHARACTER)
+		fields = list(info.getTextWithFields())
+		layoutIDs = self._maybeGetLayoutTableIds(info)
+		for field in reversed(fields):
+			if not (
+				isinstance(field, textInfos.FieldCommand)
+				and field.command == "controlStart"
+				and field.field.get("role") == controlTypes.Role.TABLE
+			):
+				# Not a table control field.
+				continue
+			attrs = field.field
+			tableID = attrs.get('table-id')
+			if tableID is None or tableID in layoutIDs:
+				continue
+			break
+		else:
+			raise LookupError("Not in a table cell")
+		try:
+			nRows = int(attrs.get("table-rowcount"))
+			nCols = int(attrs.get("table-columncount"))
+		except (TypeError, ValueError):
+			raise LookupError("Not in a table cell")
+		return (nRows, nCols)
+
 	def _getTableCellAt(self,tableID,startPos,row,column):
 		"""
 		Starting from the given start position, Locates the table cell with the given row and column coordinates and table ID.
@@ -161,10 +213,10 @@ def _getNearestTableCell(
 		# Determine destination row and column.
 		destRow = origRow
 		destCol = origCol
-		if axis == "row":
-			destRow += origRowSpan if movement == "next" else -1
-		elif axis == "column":
-			destCol += origColSpan if movement == "next" else -1
+		if axis == _Axis.ROW:
+			destRow += origRowSpan if movement == _Movement.NEXT else -1
+		elif axis == _Axis.COLUMN:
+			destCol += origColSpan if movement == _Movement.NEXT else -1
 
 		# Try and fetch the cell at these coordinates, though  if a  cell is missing, try  several more times moving the coordinates on by one cell each time
 		limit=self._missingTableCellSearchLimit
@@ -177,12 +229,68 @@ def _getNearestTableCell(
 				return self._getTableCellAt(tableID,startPos,destRow,destCol)
 			except LookupError:
 				pass
-			if axis=="row":
-				destRow+=1 if movement=="next" else -1
+			if axis == _Axis.ROW:
+				destRow += 1 if movement == _Movement.NEXT else -1
 			else:
-				destCol+=1 if movement=="next" else -1
+				destCol += 1 if movement == _Movement.NEXT else -1
 		raise LookupError
 
+	def _getFirstOrLastTableCell(
+			self,
+			tableID: _TableID,
+			startPos: textInfos.TextInfo,
+			origRow: int,
+			origCol: int,
+			origRowSpan: int,
+			origColSpan: int,
+			movement: _Movement,
+			axis: _Axis
+	) -> textInfos.TextInfo:
+		"""
+		Locates the first or last cell in current row or column given coordinates of current cell.
+		When jumping to the first row/column, It will try to set current row/column index to 1.
+		When jumping to the last row/column, it will query table dimensions and set row/column index
+		to corresponding dimension.
+		After figuring out exact coordinates of the cell it will try to jump directly to that cell,
+		or if that fails (due to missing table cell), it will walk in the opposite direction skipping missing cells
+		up to the number of times set by _missingTableCellSearchLimit set on this instance.
+		@param tableID: the ID of the table
+		@param startPos: the position in the document to start searching from.
+		@param origRow: the row number of the starting cell
+		@param origCol: the column number  of the starting cell
+		@param origRowSpan: the row span of the row of the starting cell
+		@param origColSpan: the column span of the column of the starting cell
+		@param movement: the direction ("first" or "last")
+		@param axis: the axis of movement ("row" or "column")
+		@returns: the position of the destination table cell
+		"""
+		destRow, destCol = origRow, origCol
+		if movement == _Movement.FIRST:
+			if axis == _Axis.COLUMN:
+				destCol = 1
+			else:
+				destRow = 1
+		else:
+			nRows, nCols = self._getTableDimensions(startPos)
+			if axis == _Axis.COLUMN:
+				destCol = nCols
+			else:
+				destRow = nRows
+		try:
+			return self._getTableCellAt(tableID, startPos, destRow, destCol)
+		except LookupError:
+			oppositeMovement = _Movement.PREVIOUS if movement == _Movement.LAST else _Movement.NEXT
+			return self._getNearestTableCell(
+				tableID,
+				startPos,
+				destRow,
+				destCol,
+				origRowSpan=1,
+				origColSpan=1,
+				movement=oppositeMovement,
+				axis=axis
+			)
+
 	def _tableMovementScriptHelper(
 			self,
 			movement: _Movement = _Movement.NEXT,
@@ -224,7 +332,30 @@ def _tableMovementScriptHelper(
 				origRowSpan = self._lastTableSelection.rowSpan
 
 		try:
-			info = self._getNearestTableCell(tableID, self.selection, origRow, origCol, origRowSpan, origColSpan, movement, axis)
+			if movement in {_Movement.PREVIOUS, _Movement.NEXT}:
+				info = self._getNearestTableCell(
+					tableID,
+					self.selection,
+					origRow,
+					origCol,
+					origRowSpan,
+					origColSpan,
+					movement,
+					axis
+				)
+			elif movement in {_Movement.FIRST, _Movement.LAST}:
+				info = self._getFirstOrLastTableCell(
+					tableID,
+					self.selection,
+					origRow,
+					origCol,
+					origRowSpan,
+					origColSpan,
+					movement,
+					axis
+				)
+			else:
+				raise ValueError(f"Unknown movement {movement}")
 			newTableID, newRow, newCol, newRowSpan, newColSpan = self._getTableCellCoords(info)
 		except LookupError:
 			# Translators: The message reported when a user attempts to use a table movement command
@@ -248,25 +379,45 @@ def _tableMovementScriptHelper(
 		)
 
 	def script_nextRow(self, gesture):
-		self._tableMovementScriptHelper(axis="row", movement="next")
+		self._tableMovementScriptHelper(axis=_Axis.ROW, movement=_Movement.NEXT)
 	# Translators: the description for the next table row script on browseMode documents.
 	script_nextRow.__doc__ = _("moves to the next table row")
 
 	def script_previousRow(self, gesture):
-		self._tableMovementScriptHelper(axis="row", movement="previous")
+		self._tableMovementScriptHelper(axis=_Axis.ROW, movement=_Movement.PREVIOUS)
 	# Translators: the description for the previous table row script on browseMode documents.
 	script_previousRow.__doc__ = _("moves to the previous table row")
 
 	def script_nextColumn(self, gesture):
-		self._tableMovementScriptHelper(axis="column", movement="next")
+		self._tableMovementScriptHelper(axis=_Axis.COLUMN, movement=_Movement.NEXT)
 	# Translators: the description for the next table column script on browseMode documents.
 	script_nextColumn.__doc__ = _("moves to the next table column")
 
 	def script_previousColumn(self, gesture):
-		self._tableMovementScriptHelper(axis="column", movement="previous")
+		self._tableMovementScriptHelper(axis=_Axis.COLUMN, movement=_Movement.PREVIOUS)
 	# Translators: the description for the previous table column script on browseMode documents.
 	script_previousColumn.__doc__ = _("moves to the previous table column")
 
+	def script_firstRow(self, gesture):
+		self._tableMovementScriptHelper(axis=_Axis.ROW, movement=_Movement.FIRST)
+	# Translators: the description for the first table row script on browseMode documents.
+	script_firstRow.__doc__ = _("moves to the first table row")
+
+	def script_lastRow(self, gesture):
+		self._tableMovementScriptHelper(axis=_Axis.ROW, movement=_Movement.LAST)
+	# Translators: the description for the last table row script on browseMode documents.
+	script_lastRow.__doc__ = _("moves to the last table row")
+
+	def script_firstColumn(self, gesture):
+		self._tableMovementScriptHelper(axis=_Axis.COLUMN, movement=_Movement.FIRST)
+	# Translators: the description for the first table column script on browseMode documents.
+	script_firstColumn.__doc__ = _("moves to the first table column")
+
+	def script_lastColumn(self, gesture):
+		self._tableMovementScriptHelper(axis=_Axis.COLUMN, movement=_Movement.LAST)
+	# Translators: the description for the last table column script on browseMode documents.
+	script_lastColumn.__doc__ = _("moves to the last table column")
+
 	def script_toggleIncludeLayoutTables(self,gesture):
 		# documentBase is a core module and should not depend on UI, so it is imported at run-time. (#12404)
 		import ui
@@ -287,4 +438,8 @@ def script_toggleIncludeLayoutTables(self,gesture):
 		"kb:control+alt+upArrow": "previousRow",
 		"kb:control+alt+rightArrow": "nextColumn",
 		"kb:control+alt+leftArrow": "previousColumn",
+		"kb:control+alt+pageUp": "firstRow",
+		"kb:control+alt+pageDown": "lastRow",
+		"kb:control+alt+Home": "firstColumn",
+		"kb:control+alt+End": "lastColumn",
 	}


Test patch: