Bug 417929 - nsIAccessiblTable selectRows does not unselect previously selected rows, r=marcoz, davidb, ginn, smaug, sr=roc

This commit is contained in:
Alexander Surkov 2009-09-10 11:06:54 +08:00
Родитель 5ef33c9cfd
Коммит e5ea189576
6 изменённых файлов: 491 добавлений и 208 удалений

Просмотреть файл

@ -58,6 +58,7 @@
#include "nsIServiceManager.h"
#include "nsITableLayout.h"
#include "nsITableCellLayout.h"
#include "nsFrameSelection.h"
#include "nsLayoutErrors.h"
////////////////////////////////////////////////////////////////////////////////
@ -1079,117 +1080,142 @@ nsHTMLTableAccessible::IsValidRow(PRInt32 aRow)
NS_IMETHODIMP
nsHTMLTableAccessible::SelectRow(PRInt32 aRow)
{
return SelectRowOrColumn(aRow, nsISelectionPrivate::TABLESELECTION_ROW,
PR_TRUE);
if (IsDefunct())
return NS_ERROR_FAILURE;
nsresult rv =
RemoveRowsOrColumnsFromSelection(aRow,
nsISelectionPrivate::TABLESELECTION_ROW,
PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
return AddRowOrColumnToSelection(aRow,
nsISelectionPrivate::TABLESELECTION_ROW);
}
NS_IMETHODIMP
nsHTMLTableAccessible::SelectColumn(PRInt32 aColumn)
{
return SelectRowOrColumn(aColumn, nsISelectionPrivate::TABLESELECTION_COLUMN,
PR_TRUE);
if (IsDefunct())
return NS_ERROR_FAILURE;
nsresult rv =
RemoveRowsOrColumnsFromSelection(aColumn,
nsISelectionPrivate::TABLESELECTION_COLUMN,
PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
return AddRowOrColumnToSelection(aColumn,
nsISelectionPrivate::TABLESELECTION_COLUMN);
}
NS_IMETHODIMP
nsHTMLTableAccessible::UnselectRow(PRInt32 aRow)
{
return SelectRowOrColumn(aRow, nsISelectionPrivate::TABLESELECTION_ROW,
PR_FALSE);
if (IsDefunct())
return NS_ERROR_FAILURE;
return
RemoveRowsOrColumnsFromSelection(aRow,
nsISelectionPrivate::TABLESELECTION_ROW,
PR_FALSE);
}
NS_IMETHODIMP
nsHTMLTableAccessible::UnselectColumn(PRInt32 aColumn)
{
return SelectRowOrColumn(aColumn, nsISelectionPrivate::TABLESELECTION_COLUMN,
PR_FALSE);
if (IsDefunct())
return NS_ERROR_FAILURE;
return
RemoveRowsOrColumnsFromSelection(aColumn,
nsISelectionPrivate::TABLESELECTION_COLUMN,
PR_FALSE);
}
nsresult
nsHTMLTableAccessible::SelectRowOrColumn(PRInt32 aIndex, PRUint32 aTarget,
PRBool aDoSelect)
nsHTMLTableAccessible::AddRowOrColumnToSelection(PRInt32 aIndex,
PRUint32 aTarget)
{
PRBool doSelectRow = (aTarget == nsISelectionPrivate::TABLESELECTION_ROW);
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
if (!content)
return NS_OK;
nsCOMPtr<nsIDocument> document = content->GetCurrentDoc();
NS_ENSURE_STATE(document);
nsCOMPtr<nsISelectionController> selController(
do_QueryInterface(document->GetPrimaryShell()));
NS_ENSURE_STATE(selController);
nsCOMPtr<nsISelection> selection;
selController->GetSelection(nsISelectionController::SELECTION_NORMAL,
getter_AddRefs(selection));
NS_ENSURE_STATE(selection);
PRInt32 count = 0;
nsresult rv = doSelectRow ? GetColumns(&count) : GetRows(&count);
nsITableLayout *tableLayout = nsnull;
nsresult rv = GetTableLayout(&tableLayout);
NS_ENSURE_SUCCESS(rv, rv);
for (PRInt32 index = 0; index < count; index++) {
nsCOMPtr<nsIDOMElement> cellElm;
PRInt32 column = doSelectRow ? index : aIndex;
PRInt32 row = doSelectRow ? aIndex : index;
nsCOMPtr<nsIDOMElement> cellElm;
PRInt32 startRowIdx, startColIdx, rowSpan, colSpan,
actualRowSpan, actualColSpan;
PRBool isSelected = PR_FALSE;
rv = GetCellAt(row, column, *getter_AddRefs(cellElm));
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 count = 0;
if (doSelectRow)
rv = GetColumns(&count);
else
rv = GetRows(&count);
rv = SelectCell(selection, document, cellElm, aDoSelect);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPresShell> presShell(GetPresShell());
nsRefPtr<nsFrameSelection> tableSelection =
const_cast<nsFrameSelection*>(presShell->ConstFrameSelection());
for (PRInt32 idx = 0; idx < count; idx++) {
PRInt32 rowIdx = doSelectRow ? aIndex : idx;
PRInt32 colIdx = doSelectRow ? idx : aIndex;
rv = tableLayout->GetCellDataAt(rowIdx, colIdx,
*getter_AddRefs(cellElm),
startRowIdx, startColIdx,
rowSpan, colSpan,
actualRowSpan, actualColSpan,
isSelected);
if (NS_SUCCEEDED(rv) && !isSelected) {
nsCOMPtr<nsIContent> cellContent(do_QueryInterface(cellElm));
rv = tableSelection->SelectCellElement(cellContent);
NS_ENSURE_SUCCESS(rv, rv);
}
}
return NS_OK;
}
nsresult
nsHTMLTableAccessible::SelectCell(nsISelection *aSelection,
nsIDocument *aDocument,
nsIDOMElement *aCellElement,
PRBool aDoSelect)
nsHTMLTableAccessible::RemoveRowsOrColumnsFromSelection(PRInt32 aIndex,
PRUint32 aTarget,
PRBool aIsOuter)
{
if (aDoSelect) {
nsCOMPtr<nsIDOMDocumentRange> documentRange(do_QueryInterface(aDocument));
NS_ENSURE_STATE(documentRange);
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
nsCOMPtr<nsIDOMRange> range;
documentRange->CreateRange(getter_AddRefs(range));
nsCOMPtr<nsIDOMNode> cellNode(do_QueryInterface(aCellElement));
NS_ENSURE_STATE(cellNode);
range->SelectNode(cellNode);
return aSelection->AddRange(range);
}
nsCOMPtr<nsIContent> cell(do_QueryInterface(aCellElement));
NS_ENSURE_STATE(cell);
nsCOMPtr<nsIContent> cellParent = cell->GetParent();
NS_ENSURE_STATE(cellParent);
PRInt32 offset = cellParent->IndexOf(cell);
NS_ENSURE_STATE(offset != -1);
nsCOMPtr<nsIDOMNode> parent(do_QueryInterface(cellParent));
NS_ENSURE_STATE(parent);
nsCOMPtr<nsISelection2> selection2(do_QueryInterface(aSelection));
NS_ENSURE_STATE(selection2);
nsCOMArray<nsIDOMRange> ranges;
nsresult rv = selection2->GetRangesForIntervalCOMArray(parent, offset,
parent, offset,
PR_TRUE, &ranges);
nsITableLayout *tableLayout = nsnull;
nsresult rv = GetTableLayout(&tableLayout);
NS_ENSURE_SUCCESS(rv, rv);
for (PRInt32 i = 0; i < ranges.Count(); i ++)
aSelection->RemoveRange(ranges[i]);
nsCOMPtr<nsIPresShell> presShell(GetPresShell());
nsRefPtr<nsFrameSelection> tableSelection =
const_cast<nsFrameSelection*>(presShell->ConstFrameSelection());
return NS_OK;
PRBool doUnselectRow = (aTarget == nsISelectionPrivate::TABLESELECTION_ROW);
PRInt32 count = 0;
if (doUnselectRow)
rv = GetColumns(&count);
else
rv = GetRows(&count);
PRInt32 startRowIdx = doUnselectRow ? aIndex : 0;
PRInt32 endRowIdx = doUnselectRow ? aIndex : count - 1;
PRInt32 startColIdx = doUnselectRow ? 0 : aIndex;
PRInt32 endColIdx = doUnselectRow ? count - 1 : aIndex;
if (aIsOuter)
return tableSelection->RestrictCellsToSelection(content,
startRowIdx, startColIdx,
endRowIdx, endColIdx);
return tableSelection->RemoveCellsFromSelection(content,
startRowIdx, startColIdx,
endRowIdx, endColIdx);
}
nsresult

Просмотреть файл

@ -187,26 +187,26 @@ public:
protected:
/**
* Selects or unselects row or column.
* Add row or column to selection.
*
* @param aIndex - index of row or column to be selected
* @param aTarget - indicates what should be selected, either row or column
* @param aIndex [in] index of row or column to be selected
* @param aTarget [in] indicates what should be selected, either row or column
* (see nsISelectionPrivate)
* @param aDoSelect - indicates whether row or column should selected or
* unselected
*/
nsresult SelectRowOrColumn(PRInt32 aIndex, PRUint32 aTarget, PRBool aDoSelect);
nsresult AddRowOrColumnToSelection(PRInt32 aIndex, PRUint32 aTarget);
/**
* Selects or unselects the cell.
* Removes rows or columns at the given index or outside it from selection.
*
* @param aSelection - the selection of document
* @param aDocument - the document that contains the cell
* @param aCellElement - the cell of table
* @param aDoSelect - indicates whether cell should be selected or unselected
* @param aIndex [in] row or column index
* @param aTarget [in] indicates whether row or column should unselected
* @param aIsOuter [in] indicates whether all rows or column excepting
* the given one should be unselected or the given one
* should be unselected only
*/
nsresult SelectCell(nsISelection *aSelection, nsIDocument *aDocument,
nsIDOMElement *aCellElement, PRBool aDoSelect);
nsresult RemoveRowsOrColumnsFromSelection(PRInt32 aIndex,
PRUint32 aTarget,
PRBool aIsOuter);
virtual void CacheChildren();
nsresult GetTableNode(nsIDOMNode **_retval);

Просмотреть файл

@ -101,12 +101,22 @@ function testTableIndexes(aIdentifier, aIdxes)
}
}
/**
* Constants used to describe cells states array. Values 0 and 1 are reserved
* for boolean flag indicating the cell is origin and it is whether unselected
* or selected.
*/
const kRowSpanned = 2; // Indicates the cell is not origin and row spanned
const kColSpanned = 4; // Indicates the cell is not origin and column spanned
const kSpanned = kRowSpanned | kColSpanned;
/**
* Test table getters selection methods.
*
* @param aIdentifier [in] table accessible identifier
* @param aCellsArray [in] two dimensional array (row X columns) of selected
* cells states.
* @param aCellsArray [in] two dimensional array (row X columns) of cells
* states (either boolean (selected/unselected) if cell is
* origin, or one of constants defined above).
* @param aMsg [in] text appended before every message
*/
function testTableSelection(aIdentifier, aCellsArray, aMsg)
@ -204,9 +214,9 @@ function testTableSelection(aIdentifier, aCellsArray, aMsg)
// isCellSelected test
for (var rowIdx = 0; rowIdx < rowsCount; rowIdx++) {
for (var colIdx = 0; colIdx < colsCount; colIdx++) {
if (aCellsArray[rowIdx][colIdx] == undefined)
if (!isOrigCell(aCellsArray[rowIdx][colIdx]))
continue;
is(acc.isCellSelected(rowIdx, colIdx), aCellsArray[rowIdx][colIdx],
msg + "Wrong selection state of cell at " + rowIdx + " row and " +
colIdx + " column for " + prettyName(aIdentifier));
@ -237,7 +247,7 @@ function testTableSelection(aIdentifier, aCellsArray, aMsg)
// selected states tests
for (var rowIdx = 0; rowIdx < rowsCount; rowIdx++) {
for (var colIdx = 0; colIdx < colsCount; colIdx++) {
if (aCellsArray[rowIdx][colIdx] == undefined)
if (!isOrigCell(aCellsArray[rowIdx][colIdx]))
continue;
var cell = acc.cellRefAt(rowIdx, colIdx);
@ -261,8 +271,11 @@ function testUnselectTableColumn(aIdentifier, aColIdx, aCellsArray)
var rowsCount = aCellsArray.length;
for (var rowIdx = 0; rowIdx < rowsCount; rowIdx++) {
if (aCellsArray[rowIdx][aColIdx] != undefined)
aCellsArray[rowIdx][aColIdx] = false;
var cellState = aCellsArray[rowIdx][aColIdx];
// Unselect origin cell.
var [origRowIdx, origColIdx] =
getOrigRowAndColumn(aCellsArray, rowIdx, aColIdx);
aCellsArray[origRowIdx][origColIdx] = false;
}
acc.unselectColumn(aColIdx);
@ -284,8 +297,42 @@ function testSelectTableColumn(aIdentifier, aColIdx, aCellsArray)
for (var rowIdx = 0; rowIdx < rowsCount; rowIdx++) {
for (var colIdx = 0; colIdx < colsCount; colIdx++) {
if (aCellsArray[rowIdx][colIdx] != undefined)
aCellsArray[rowIdx][colIdx] = (colIdx == aColIdx);
var cellState = aCellsArray[rowIdx][colIdx];
if (colIdx == aColIdx) { // select target column
if (isOrigCell(cellState)) {
// Select the cell if it is origin.
aCellsArray[rowIdx][colIdx] = true;
} else {
// If the cell is spanned then search origin cell and select it.
var [origRowIdx, origColIdx] = getOrigRowAndColumn(aCellsArray,
rowIdx, colIdx);
aCellsArray[origRowIdx][origColIdx] = true;
}
} else if (isOrigCell(cellState)) { // unselect other columns
if (colIdx > aColIdx) {
// Unselect the cell if traversed column index is greater than column
// index of target cell.
aCellsArray[rowIdx][colIdx] = false;
} else if (!isColSpannedCell(aCellsArray[rowIdx][aColIdx])) {
// Unselect the cell if the target cell is not row spanned.
aCellsArray[rowIdx][colIdx] = false;
} else {
// Unselect the cell if it is not spanned to the target cell.
for (var spannedColIdx = colIdx + 1; spannedColIdx < aColIdx;
spannedColIdx++) {
var spannedCellState = aCellsArray[rowIdx][spannedColIdx];
if (!isRowSpannedCell(spannedCellState)) {
aCellsArray[rowIdx][colIdx] = false;
break;
}
}
}
}
}
}
@ -305,8 +352,10 @@ function testUnselectTableRow(aIdentifier, aRowIdx, aCellsArray)
var colsCount = aCellsArray[0].length;
for (var colIdx = 0; colIdx < colsCount; colIdx++) {
if (aCellsArray[aRowIdx][colIdx] != undefined)
aCellsArray[aRowIdx][colIdx] = false;
// Unselect origin cell.
var [origRowIdx, origColIdx] = getOrigRowAndColumn(aCellsArray,
aRowIdx, colIdx);
aCellsArray[origRowIdx][origColIdx] = false;
}
acc.unselectRow(aRowIdx);
@ -328,8 +377,43 @@ function testSelectTableRow(aIdentifier, aRowIdx, aCellsArray)
for (var rowIdx = 0; rowIdx < rowsCount; rowIdx++) {
for (var colIdx = 0; colIdx < colsCount; colIdx++) {
if (aCellsArray[rowIdx][colIdx] != undefined)
aCellsArray[rowIdx][colIdx] = (rowIdx == aRowIdx);
var cellState = aCellsArray[rowIdx][colIdx];
if (rowIdx == aRowIdx) { // select the given row
if (isOrigCell(cellState)) {
// Select the cell if it is origin.
aCellsArray[rowIdx][colIdx] = true;
} else {
// If the cell is spanned then search origin cell and select it.
var [origRowIdx, origColIdx] = getOrigRowAndColumn(aCellsArray,
rowIdx, colIdx);
aCellsArray[origRowIdx][origColIdx] = true;
}
} else if (isOrigCell(cellState)) { // unselect other rows
if (rowIdx > aRowIdx) {
// Unselect the cell if traversed row index is greater than row
// index of target cell.
aCellsArray[rowIdx][colIdx] = false;
} else if (!isRowSpannedCell(aCellsArray[aRowIdx][colIdx])) {
// Unselect the cell if the target cell is not row spanned.
aCellsArray[rowIdx][colIdx] = false;
} else {
// Unselect the cell if it is not spanned to the target cell.
for (var spannedRowIdx = rowIdx + 1; spannedRowIdx < aRowIdx;
spannedRowIdx++) {
var spannedCellState = aCellsArray[spannedRowIdx][colIdx];
if (!isRowSpannedCell(spannedCellState)) {
aCellsArray[rowIdx][colIdx] = false;
break;
}
}
}
}
}
}
@ -337,3 +421,52 @@ function testSelectTableRow(aIdentifier, aRowIdx, aCellsArray)
testTableSelection(aIdentifier, aCellsArray,
"Select " + aRowIdx + " row: ");
}
////////////////////////////////////////////////////////////////////////////////
// private implementation
/**
* Return row and column of orig cell for the given spanned cell.
*/
function getOrigRowAndColumn(aCellsArray, aRowIdx, aColIdx)
{
var cellState = aCellsArray[aRowIdx][aColIdx];
var origRowIdx = aRowIdx, origColIdx = aColIdx;
if (isRowSpannedCell(cellState)) {
for (var prevRowIdx = aRowIdx - 1; prevRowIdx >= 0; prevRowIdx--) {
var prevCellState = aCellsArray[prevRowIdx][aColIdx];
if (!isRowSpannedCell(prevCellState)) {
origRowIdx = prevRowIdx;
break;
}
}
}
if (isColSpannedCell(cellState)) {
for (var prevColIdx = aColIdx - 1; prevColIdx >= 0; prevColIdx--) {
var prevCellState = aCellsArray[aRowIdx][prevColIdx];
if (!isColSpannedCell(prevCellState)) {
origColIdx = prevColIdx;
break;
}
}
}
return [origRowIdx, origColIdx];
}
function isOrigCell(aCellState)
{
return !(aCellState & (kRowSpanned | kColSpanned));
}
function isRowSpannedCell(aCellState)
{
return aCellState & kRowSpanned;
}
function isColSpannedCell(aCellState)
{
return aCellState & kColSpanned;
}

Просмотреть файл

@ -24,59 +24,35 @@
{
var cellsArray =
[
[false, false, false, undefined, false, false, false, false],
[false, false, false, false, false, false, false, undefined],
[false, false, undefined, false, false, false, false, undefined],
[false, false, undefined, false, false, undefined, false, undefined]
[false, false, false, kColSpanned, false, false, false, false],
[false, false, false, false, false, false, false, kRowSpanned],
[false, false, kColSpanned, false, false, false, false, kRowSpanned],
[false, kRowSpanned, kSpanned, false, false, kRowSpanned, false, kRowSpanned]
];
testTableSelection("table", cellsArray);
testSelectTableRow("table", 0, cellsArray);
var rowsCount = 4;
for (var rowIdx = 0; rowIdx < rowsCount; rowIdx++)
testSelectTableRow("table", rowIdx, cellsArray);
for (var rowIdx = 0; rowIdx < rowsCount; rowIdx++) {
testSelectTableRow("table", rowIdx, cellsArray);
testUnselectTableRow("table", rowIdx, cellsArray);
}
var columsCount = 8;
for (var colIdx = 0; colIdx < columsCount; colIdx++)
testSelectTableColumn("table", colIdx, cellsArray);
for (var colIdx = 0; colIdx < columsCount; colIdx++) {
testSelectTableColumn("table", colIdx, cellsArray);
testUnselectTableColumn("table", colIdx, cellsArray);
}
var accTable = getAccessible("table", [nsIAccessibleTable]);
for (var i = 0; i < 4; i++) {
accTable.selectRow(i);
for (var j = 0; j < 4; j++)
if (i == j)
ok(accTable.isRowSelected(i),"row not selected");
else
todo(!accTable.isRowSelected(i),"row selected");
}
todo_is(accTable.selectedRowsCount, 1, "only one row should be selected");
todo_is(accTable.getSelectedRows({}).length, 1,
"only one row should be selected");
for (var i = 0; i < 4; i++) {
accTable.unselectRow(i);
ok(!accTable.isRowSelected(i), "row still selected");
}
todo_is(accTable.getSelectedCells({}).length, 0, "no cell selected");
todo_is(accTable.selectedCellsCount, 0, "no cell selected");
var s = window.getSelection();
if (s.rangeCount > 0)
s.removeAllRanges();
is(accTable.getSelectedCells({}).length, 0, "no cell selected");
is(accTable.selectedCellsCount, 0, "no cell selected");
for (var i = 0; i < 8; i++) {
accTable.selectColumn(i);
for (var j = 0; j < 8; j++)
if (i ==j)
ok(accTable.isColumnSelected(i),"column not selected");
else
todo(!accTable.isColumnSelected(i),"column is selected");
}
todo_is(accTable.selectedColumnsCount, 1,
"only one column should be selected");
todo_is(accTable.getSelectedColumns({}).length, 1,
"only one column should be selected");
for (var i = 0; i < 8; i++) {
accTable.unselectColumn(i);
ok(!accTable.isColumnSelected(i),"column still selected");
}
is(accTable.selectedColumnsCount, 0, "no column should be selected");
ok(!accTable.isProbablyForLayout(), "table is not for layout");
SimpleTest.finish();
}
@ -96,6 +72,11 @@
title="nsHTMLTableAccessible::GetSelectedCells contains index duplicates for spanned rows or columns">
Mozilla Bug 501635
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=417929"
title="nsIAccessiblTable selectRows does not unselect previously selected rows">
Mozilla Bug 417929
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>

Просмотреть файл

@ -49,10 +49,10 @@
#include "nsIRange.h"
// IID for the nsFrameSelection interface
// 0ea74459-e3f9-48b0-8aa4-5dfef53bf1f7
// 3c6ae2d0-4cf1-44a1-9e9d-2411867f19c6
#define NS_FRAME_SELECTION_IID \
{ 0xea74459, 0xe3f9, 0x48b0, \
{ 0x8a, 0xa4, 0x5d, 0xfe, 0xf5, 0x3b, 0xf1, 0xf7 } }
{ 0x3c6ae2d0, 0x4cf1, 0x44a1, \
{ 0x9e, 0x9d, 0x24, 0x11, 0x86, 0x7f, 0x19, 0xc6 } }
#ifdef IBMBIDI // Constant for Set/Get CaretBidiLevel
#define BIDI_LEVEL_UNDEFINED 0x80
@ -292,6 +292,58 @@ public:
PRInt32 aTarget,
nsMouseEvent *aMouseEvent);
/**
* Add cell to the selection.
*
* @param aCell [in] HTML td element.
*/
virtual nsresult SelectCellElement(nsIContent *aCell);
/**
* Add cells to the selection inside of the given cells range.
*
* @param aTable [in] HTML table element
* @param aStartRowIndex [in] row index where the cells range starts
* @param aStartColumnIndex [in] column index where the cells range starts
* @param aEndRowIndex [in] row index where the cells range ends
* @param aEndColumnIndex [in] column index where the cells range ends
*/
virtual nsresult AddCellsToSelection(nsIContent *aTable,
PRInt32 aStartRowIndex,
PRInt32 aStartColumnIndex,
PRInt32 aEndRowIndex,
PRInt32 aEndColumnIndex);
/**
* Remove cells from selection inside of the given cell range.
*
* @param aTable [in] HTML table element
* @param aStartRowIndex [in] row index where the cells range starts
* @param aStartColumnIndex [in] column index where the cells range starts
* @param aEndRowIndex [in] row index where the cells range ends
* @param aEndColumnIndex [in] column index where the cells range ends
*/
virtual nsresult RemoveCellsFromSelection(nsIContent *aTable,
PRInt32 aStartRowIndex,
PRInt32 aStartColumnIndex,
PRInt32 aEndRowIndex,
PRInt32 aEndColumnIndex);
/**
* Remove cells from selection outside of the given cell range.
*
* @param aTable [in] HTML table element
* @param aStartRowIndex [in] row index where the cells range starts
* @param aStartColumnIndex [in] column index where the cells range starts
* @param aEndRowIndex [in] row index where the cells range ends
* @param aEndColumnIndex [in] column index where the cells range ends
*/
virtual nsresult RestrictCellsToSelection(nsIContent *aTable,
PRInt32 aStartRowIndex,
PRInt32 aStartColumnIndex,
PRInt32 aEndRowIndex,
PRInt32 aEndColumnIndex);
/** StartAutoScrollTimer is responsible for scrolling views so that aPoint is always
* visible, and for selecting any frame that contains aPoint. The timer will also reset
* itself to fire again if we have not scrolled to the end of the document.
@ -626,6 +678,11 @@ private:
nsresult SelectBlockOfCells(nsIContent *aStartNode, nsIContent *aEndNode);
nsresult SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget);
nsresult UnselectCells(nsIContent *aTable,
PRInt32 aStartRowIndex, PRInt32 aStartColumnIndex,
PRInt32 aEndRowIndex, PRInt32 aEndColumnIndex,
PRBool aRemoveOutsideOfCellRange);
nsresult GetCellIndexes(nsIContent *aCell, PRInt32 &aRowIndex, PRInt32 &aColIndex);
// Get our first range, if its first selected node is a cell. If this does
@ -641,7 +698,6 @@ private:
nsIContent* IsInSameTable(nsIContent *aContent1, nsIContent *aContent2) const;
// Might return null
nsIContent* GetParentTable(nsIContent *aCellNode) const;
nsresult SelectCellElement(nsIContent* aCellElement);
nsresult CreateAndAddRange(nsINode *aParentNode, PRInt32 aOffset);
nsresult ClearNormalSelection();

Просмотреть файл

@ -2766,69 +2766,134 @@ nsFrameSelection::SelectBlockOfCells(nsIContent *aStartCell, nsIContent *aEndCel
result = GetCellIndexes(aEndCell, endRowIndex, endColIndex);
if(NS_FAILED(result)) return result;
// Check that |table| is a table.
if (!GetTableLayout(table)) return NS_ERROR_FAILURE;
PRInt32 curRowIndex, curColIndex;
if (mDragSelectingCells)
{
// Drag selecting: remove selected cells outside of new block limits
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
if (!mDomSelections[index])
return NS_ERROR_NULL_POINTER;
// Strong reference because we sometimes remove the range
nsCOMPtr<nsIRange> range = GetFirstCellRange();
nsIContent* cellNode = GetFirstSelectedContent(range);
NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range");
PRInt32 minRowIndex = PR_MIN(startRowIndex, endRowIndex);
PRInt32 maxRowIndex = PR_MAX(startRowIndex, endRowIndex);
PRInt32 minColIndex = PR_MIN(startColIndex, endColIndex);
PRInt32 maxColIndex = PR_MAX(startColIndex, endColIndex);
while (cellNode)
{
result = GetCellIndexes(cellNode, curRowIndex, curColIndex);
if (NS_FAILED(result)) return result;
#ifdef DEBUG_TABLE_SELECTION
if (!range)
printf("SelectBlockOfCells -- range is null\n");
#endif
if (range &&
(curRowIndex < minRowIndex || curRowIndex > maxRowIndex ||
curColIndex < minColIndex || curColIndex > maxColIndex))
{
mDomSelections[index]->RemoveRange(range);
// Since we've removed the range, decrement pointer to next range
mSelectedCellIndex--;
}
range = GetNextCellRange();
cellNode = GetFirstSelectedContent(range);
NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range");
}
UnselectCells(table, startRowIndex, startColIndex, endRowIndex, endColIndex,
PR_TRUE);
}
nsCOMPtr<nsIDOMElement> cellElement;
PRInt32 rowSpan, colSpan, actualRowSpan, actualColSpan;
PRBool isSelected;
// Note that we select block in the direction of user's mouse dragging,
// which means start cell may be after the end cell in either row or column
PRInt32 row = startRowIndex;
return AddCellsToSelection(table, startRowIndex, startColIndex,
endRowIndex, endColIndex);
}
nsresult
nsFrameSelection::UnselectCells(nsIContent *aTableContent,
PRInt32 aStartRowIndex,
PRInt32 aStartColumnIndex,
PRInt32 aEndRowIndex,
PRInt32 aEndColumnIndex,
PRBool aRemoveOutsideOfCellRange)
{
PRInt8 index =
GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
if (!mDomSelections[index])
return NS_ERROR_NULL_POINTER;
nsITableLayout *tableLayout = GetTableLayout(aTableContent);
if (!tableLayout)
return NS_ERROR_FAILURE;
PRInt32 minRowIndex = PR_MIN(aStartRowIndex, aEndRowIndex);
PRInt32 maxRowIndex = PR_MAX(aStartRowIndex, aEndRowIndex);
PRInt32 minColIndex = PR_MIN(aStartColumnIndex, aEndColumnIndex);
PRInt32 maxColIndex = PR_MAX(aStartColumnIndex, aEndColumnIndex);
// Strong reference because we sometimes remove the range
nsCOMPtr<nsIRange> range = GetFirstCellRange();
nsIContent* cellNode = GetFirstSelectedContent(range);
NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range");
PRInt32 curRowIndex, curColIndex;
while (cellNode)
{
nsresult result = GetCellIndexes(cellNode, curRowIndex, curColIndex);
if (NS_FAILED(result))
return result;
#ifdef DEBUG_TABLE_SELECTION
if (!range)
printf("RemoveCellsToSelection -- range is null\n");
#endif
if (range) {
if (aRemoveOutsideOfCellRange) {
if (curRowIndex < minRowIndex || curRowIndex > maxRowIndex ||
curColIndex < minColIndex || curColIndex > maxColIndex) {
mDomSelections[index]->RemoveRange(range);
// Since we've removed the range, decrement pointer to next range
mSelectedCellIndex--;
}
} else {
// Remove cell from selection if it belongs to the given cells range or
// it is spanned onto the cells range.
nsCOMPtr<nsIDOMElement> cellElement;
PRInt32 origRowIndex, origColIndex, rowSpan, colSpan,
actualRowSpan, actualColSpan;
PRBool isSelected;
result = tableLayout->GetCellDataAt(curRowIndex, curColIndex,
*getter_AddRefs(cellElement),
origRowIndex, origColIndex,
rowSpan, colSpan,
actualRowSpan, actualColSpan,
isSelected);
if (NS_FAILED(result))
return result;
if (origRowIndex <= maxRowIndex &&
origRowIndex + actualRowSpan - 1 >= minRowIndex &&
origColIndex <= maxColIndex &&
origColIndex + actualColSpan - 1 >= minColIndex) {
mDomSelections[index]->RemoveRange(range);
// Since we've removed the range, decrement pointer to next range
mSelectedCellIndex--;
}
}
}
range = GetNextCellRange();
cellNode = GetFirstSelectedContent(range);
NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range");
}
return NS_OK;
}
nsresult
nsFrameSelection::AddCellsToSelection(nsIContent *aTableContent,
PRInt32 aStartRowIndex,
PRInt32 aStartColumnIndex,
PRInt32 aEndRowIndex,
PRInt32 aEndColumnIndex)
{
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
if (!mDomSelections[index])
return NS_ERROR_NULL_POINTER;
// Get TableLayout interface to access cell data based on cellmap location
// frames are not ref counted, so don't use an nsCOMPtr
nsITableLayout *tableLayoutObject = GetTableLayout(aTableContent);
if (!tableLayoutObject) // Check that |table| is a table.
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMElement> cellElement;
PRInt32 rowSpan, colSpan, actualRowSpan, actualColSpan,
curRowIndex, curColIndex;
PRBool isSelected;
nsresult result = NS_OK;
PRInt32 row = aStartRowIndex;
while(PR_TRUE)
{
PRInt32 col = startColIndex;
PRInt32 col = aStartColumnIndex;
while(PR_TRUE)
{
// Get TableLayout interface to access cell data based on cellmap location
// frames are not ref counted, so don't use an nsCOMPtr
nsITableLayout *tableLayoutObject = GetTableLayout(table);
if (!tableLayoutObject) return NS_ERROR_FAILURE;
result = tableLayoutObject->GetCellDataAt(row, col, *getter_AddRefs(cellElement),
curRowIndex, curColIndex, rowSpan, colSpan,
actualRowSpan, actualColSpan, isSelected);
@ -2844,16 +2909,16 @@ printf("SelectBlockOfCells -- range is null\n");
if (NS_FAILED(result)) return result;
}
// Done when we reach end column
if (col == endColIndex) break;
if (col == aEndColumnIndex) break;
if (startColIndex < endColIndex)
if (aStartColumnIndex < aEndColumnIndex)
col ++;
else
col--;
};
if (row == endRowIndex) break;
if (row == aEndRowIndex) break;
if (startRowIndex < endRowIndex)
if (aStartRowIndex < aEndRowIndex)
row++;
else
row--;
@ -2861,6 +2926,28 @@ printf("SelectBlockOfCells -- range is null\n");
return result;
}
nsresult
nsFrameSelection::RemoveCellsFromSelection(nsIContent *aTable,
PRInt32 aStartRowIndex,
PRInt32 aStartColumnIndex,
PRInt32 aEndRowIndex,
PRInt32 aEndColumnIndex)
{
return UnselectCells(aTable, aStartRowIndex, aStartColumnIndex,
aEndRowIndex, aEndColumnIndex, PR_FALSE);
}
nsresult
nsFrameSelection::RestrictCellsToSelection(nsIContent *aTable,
PRInt32 aStartRowIndex,
PRInt32 aStartColumnIndex,
PRInt32 aEndRowIndex,
PRInt32 aEndColumnIndex)
{
return UnselectCells(aTable, aStartRowIndex, aStartColumnIndex,
aEndRowIndex, aEndColumnIndex, PR_TRUE);
}
nsresult
nsFrameSelection::SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget)
{