public void setKeyboardSelectedRow(int index, final boolean stealFocus, final boolean forceUpdate) {
        // Early exit if disabled.
        if (KeyboardSelectionPolicy.DISABLED == keyboardSelectionPolicy) {
            return;
        }

        // Clip the row index if the paging policy is limited.
        if (keyboardPagingPolicy.isLimitedToRange()) {
            // index will be 0 if visible item count is 0.
            index = Math.max(0, Math.min(index, getVisibleItemCount() - 1));
        }

        // The user touched the view.
        ensurePendingState().viewTouched = true;

        // Early exit if the keyboard selected row has not changed and the
        // keyboard selected value is already set.
        if (!forceUpdate && getKeyboardSelectedRow() == index && getKeyboardSelectedRowValue() != null) {
            return;
        }

        // Trim to within bounds.
        final int pageStart = getPageStart();
        final int pageSize = getPageSize();
        final int rowCount = getRowCount();
        int absIndex = pageStart + index;
        if (absIndex >= rowCount && isRowCountExact()) {
            absIndex = rowCount - 1;
        }
        index = Math.max(0, absIndex) - pageStart;
        if (keyboardPagingPolicy.isLimitedToRange()) {
            index = Math.max(0, Math.min(index, pageSize - 1));
        }

        // Select the new index.
        int newPageStart = pageStart;
        int newPageSize = pageSize;
        final PendingState<T> pending = ensurePendingState();
        pending.keyboardSelectedRow = 0;
        pending.keyboardSelectedRowValue = null;
        pending.keyboardSelectedRowChanged = true;
        if (index >= 0 && index < pageSize) {
            pending.keyboardSelectedRow = index;
            pending.keyboardSelectedRowValue = index < pending.getRowDataSize()
                    ? ensurePendingState().getRowDataValue(index)
                    : null;
            pending.keyboardStealFocus = stealFocus;
            return;
        } else if (KeyboardPagingPolicy.CHANGE_PAGE == keyboardPagingPolicy) {
            // Go to previous page.
            while (index < 0) {
                final int shift = Math.min(pageSize, newPageStart);
                newPageStart -= shift;
                index += shift;
            }

            // Go to next page.
            while (index >= pageSize) {
                newPageStart += pageSize;
                index -= pageSize;
            }
        } else if (KeyboardPagingPolicy.INCREASE_RANGE == keyboardPagingPolicy) {
            // Increase range at the beginning.
            while (index < 0) {
                final int shift = Math.min(PAGE_INCREMENT, newPageStart);
                newPageSize += shift;
                newPageStart -= shift;
                index += shift;
            }

            // Increase range at the end.
            while (index >= newPageSize) {
                newPageSize += PAGE_INCREMENT;
            }
            if (isRowCountExact()) {
                newPageSize = Math.min(newPageSize, rowCount - newPageStart);
                if (index >= rowCount) {
                    index = rowCount - 1;
                }
            }
        }

        // Update the range if it changed.
        if (newPageStart != pageStart || newPageSize != pageSize) {
            pending.keyboardSelectedRow = index;
            setVisibleRange(new Range(newPageStart, newPageSize), false, false);
        }
    }
Set the row index of the keyboard selected element.

@param index       the row index
@param stealFocus  true to steal focus
@param forceUpdate force the update even if the row didn't change
stroom-core-client-widget/src/main/java/com/google/gwt/user/cellview/client/HasDataPresenter.java