Select table column and row support (20973). r=mjudge

This commit is contained in:
cmanske%netscape.com 2000-05-16 03:03:13 +00:00
Родитель cde1ff4429
Коммит 08012f4089
5 изменённых файлов: 759 добавлений и 222 удалений

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

@ -74,7 +74,7 @@
#include "nsISelectionController.h"//for the enums
#define STATUS_CHECK_RETURN_MACRO() {if (!mTracker) return NS_ERROR_FAILURE;}
//#define DEBUG_TABLE 1
#define DEBUG_TABLE 1
//static NS_DEFINE_IID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
static NS_DEFINE_IID(kCGenContentIteratorCID, NS_GENERATEDCONTENTITERATOR_CID);
@ -277,7 +277,7 @@ public:
NS_IMETHOD SetMouseDownState(PRBool aState);
NS_IMETHOD GetMouseDownState(PRBool *aState);
NS_IMETHOD GetTableCellSelection(PRBool *aState){if (aState){*aState = mSelectingTableCellMode; return NS_OK;}return NS_ERROR_NULL_POINTER;}
NS_IMETHOD GetTableCellSelection(PRBool *aState){if (aState){*aState = mSelectingTableCellMode != 0; return NS_OK;}return NS_ERROR_NULL_POINTER;}
NS_IMETHOD GetTableCellSelectionStyleColor(const nsStyleColor **aStyleColor);
NS_IMETHOD GetSelection(SelectionType aType, nsIDOMSelection **aDomSelection);
@ -336,23 +336,28 @@ private:
nsDOMSelection *mDomSelections[nsISelectionController::NUM_SELECTIONTYPES];
nsIScrollableView *GetScrollView(){return mScrollView;}
// Table selection support. Unfortunately, we can't get at nsITableCellLayout
// and nsITableLayout through nsIFrame, so we have to duplicate editor code here
// Table selection support.
// Interfaces that let us get info based on cellmap locations
nsITableLayout* GetTableLayout(nsIContent *aTableContent);
nsITableCellLayout* GetCellLayout(nsIContent *aCellContent);
nsresult SelectBlockOfCells(nsIContent *aEndNode);
nsresult SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget);
nsresult GetCellIndexes(nsIContent *aCell, PRInt32 &aRowIndex, PRInt32 &aColIndex);
nsresult GetFirstSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange);
nsresult GetNextSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange);
nsresult GetFirstCellNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aCellNode);
// aTableNode may be null if table isn't needed to be returned
PRBool IsInSameTable(nsIContent *aContent1, nsIContent *aContent2, nsIContent **aTableNode);
nsresult GetParentTable(nsIContent *aCellNode, nsIContent **aTableNode);
nsresult SelectCellElement(nsIDOMElement* aCellElement);
nsresult CreateAndAddRange(nsIDOMNode *aParentNode, PRInt32 aOffset);
nsCOMPtr<nsIContent> mStartSelectedCell;
nsCOMPtr<nsIContent> mEndSelectedCell;
PRBool mSelectingTableCells;
PRBool mSelectingTableCellMode;
PRInt32 mSelectedCellIndex;
PRBool mSelectingTableCells;
PRUint32 mSelectingTableCellMode;
PRInt32 mSelectedCellIndex;
//batching
PRInt32 mBatching;
@ -781,7 +786,7 @@ nsSelection::nsSelection()
mHint = HINTLEFT;
sInstanceCount ++;
mSelectingTableCells = PR_FALSE;
mSelectingTableCellMode = PR_FALSE;
mSelectingTableCellMode = 0;
mSelectedCellIndex = 0;
//AUTO COPY REGISTRATION
@ -1537,7 +1542,7 @@ nsSelection::HandleClick(nsIContent *aNewFocus, PRUint32 aContentOffset,
// Don't take focus when dragging off of a table
if (!mSelectingTableCells)
{
mSelectingTableCellMode = PR_FALSE;
mSelectingTableCellMode = 0;
return TakeFocus(aNewFocus, aContentOffset, aContentEndOffset, aContinueSelection, aMultipleSelection);
}
@ -1994,6 +1999,32 @@ static PRBool IsCell(nsIContent *aContent)
return (tag != 0 && tag == nsSelection::sCellAtom);
}
nsITableCellLayout*
nsSelection::GetCellLayout(nsIContent *aCellContent)
{
// Get frame for cell
nsIFrame *cellFrame = nsnull;
nsresult result = GetTracker()->GetPrimaryFrameFor(aCellContent, &cellFrame);
if (!cellFrame) return nsnull;
nsITableCellLayout *cellLayoutObject = nsnull;
result = cellFrame->QueryInterface(NS_GET_IID(nsITableCellLayout), (void**)(&cellLayoutObject));
if (NS_FAILED(result)) return nsnull;
return cellLayoutObject;
}
nsITableLayout*
nsSelection::GetTableLayout(nsIContent *aTableContent)
{
// Get frame for table
nsIFrame *tableFrame = nsnull;
nsresult result = GetTracker()->GetPrimaryFrameFor(aTableContent, &tableFrame);
if (!tableFrame) return nsnull;
nsITableLayout *tableLayoutObject = nsnull;
result = tableFrame->QueryInterface(NS_GET_IID(nsITableLayout), (void**)(&tableLayoutObject));
if (NS_FAILED(result)) return nsnull;
return tableLayoutObject;
}
nsresult
nsSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOffset, PRUint32 aTarget, nsMouseEvent *aMouseEvent)
{
@ -2016,52 +2047,60 @@ nsSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOf
nsCOMPtr<nsIDOMNode> selectedNode = do_QueryInterface(selectedContent);
if (!selectedNode) return NS_ERROR_FAILURE;
// Stack-class to wrap all table selection changes in
// BeginBatchChanges() / EndBatchChanges()
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
nsSelectionBatcher selectionBatcher(mDomSelections[index]);
// When doing table selection, always set the direction to next
// so we can be sure that anchorNode's offset always points to the selected cell
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
mDomSelections[index]->SetDirection(eDirNext);
// Stack-class to wrap all table selection changes in
// BeginBatchChanges() / EndBatchChanges()
nsSelectionBatcher selectionBatcher(mDomSelections[index]);
if (mSelectingTableCells)
{
#ifdef DEBUG_TABLE
printf("HandleTableSelection: dragging mouse\n");
#endif
// We are drag-selecting
if (aTarget == TABLESELECTION_CELL)
if (aTarget != TABLESELECTION_TABLE)
{
// aTarget can be any "cell mode",
// so we can easily drag-select rows and columns
// Once user gets the col or row hit area onclick,
// they can drift into any cell to stay in col/row mode
// (aTarget will be TABLESELECTION_CELL)
// If dragging in the same cell as last event, do nothing
if (mEndSelectedCell == selectedContent)
return NS_OK;
if (mSelectingTableCellMode == TABLESELECTION_ROW ||
mSelectingTableCellMode == TABLESELECTION_COLUMN)
{
#ifdef DEBUG_TABLE
printf("HandleTableSelection: Dragged into a new cell\n");
printf("HandleTableSelection: Dragged into a new column or row\n");
#endif
// Reselect block of cells to new end location
return SelectBlockOfCells(selectedContent);
// Append anthor row or columns to the selection
return SelectRowOrColumn(selectedContent, mSelectingTableCellMode);
}
else if (mSelectingTableCellMode == TABLESELECTION_CELL)
{
#ifdef DEBUG_TABLE
printf("HandleTableSelection: Dragged into a new cell\n");
#endif
// Reselect block of cells to new end location
return SelectBlockOfCells(selectedContent);
}
}
// Do nothing if dragging in table, but outside a cell
return NS_OK;
}
else
{
// Not dragging -- mouse event is a click
#ifdef DEBUG_TABLE
printf("HandleTableSelection: CLICK event\n");
#endif
if (aTarget == TABLESELECTION_TABLE)
{
//TODO: Currently selects when clicked between cells,
// should we restrict to only around border?
// *** How do we get location data for cell and click?
mSelectingTableCells = PR_FALSE;
mStartSelectedCell = nsnull;
mEndSelectedCell = nsnull;
#ifdef DEBUG_cmanske
#ifdef DEBUG_TABLE
//#ifdef DEBUG_TABLE
printf("HandleTableSelection: CLICK event\n");
printf("HandleTableSelection: Selecting Table\n");
{
nsIFrame *frame = nsnull;
@ -2070,15 +2109,42 @@ printf("HandleTableSelection: Selecting Table\n");
{
nsRect rect1;
frame->GetRect(rect1);
printf("Table frame orgin: x=%d, y=%d\n", rect1.x, rect1.y);
printf("Frame rect: x=%d, y=%d, right=%d, bottom=%d\n", rect1.x, rect1.y, rect1.XMost(), rect1.YMost());
nsRect rect2 = rect1;
mDomSelections[index]->GetFrameToRootViewOffset(frame, &rect2.x, &rect2.y);
printf("Translated frame orgin: x=%d, y=%d\n", rect2.x, rect2.y);
printf("Mouse was clicked at: x=%d, y=%d\n", aMouseEvent->point.x, aMouseEvent->point.y);
printf("*** Widget-relative (refPoint): x=%d, y=%d\n", aMouseEvent->refPoint.x, aMouseEvent->refPoint.y);
}
/*
Useful frame methods:
GetRect(nsRect& aRect)
GetSize(nsSize& aSize)
*/
}
//#endif
#endif
#endif
if (aTarget == TABLESELECTION_ROW || aTarget == TABLESELECTION_COLUMN)
{
// Start drag-selecting mode so multiple rows/cols can be selected
mSelectingTableCells = PR_TRUE;
mSelectingTableCellMode = aTarget; //only shut off on mouse up.
// Force new selection block
mStartSelectedCell = nsnull;
mDomSelections[index]->ClearSelection();
return SelectRowOrColumn(selectedContent, aTarget);
}
else if (aTarget == TABLESELECTION_TABLE)
{
//TODO: We currently select entire table when clicked between cells,
// should we restrict to only around border?
// *** How do we get location data for cell and click?
mSelectingTableCells = PR_FALSE;
mStartSelectedCell = nsnull;
mEndSelectedCell = nsnull;
// Select the table
mDomSelections[index]->ClearSelection();
return CreateAndAddRange(parentNode, aContentOffset);
@ -2117,10 +2183,7 @@ printf("Mouse was clicked at: x=%d, y=%d\n", aMouseEvent->point.x, aMouseEvent->
result = parentContent->ChildAt(offset, *getter_AddRefs(childContent));
if (NS_FAILED(result)) return result;
if (childContent && IsCell(childContent))
{
previousCellParent = parent;
}
// We're done if we didn't find parent of a previously-selected cell
if (!previousCellParent) break;
@ -2176,7 +2239,7 @@ printf("Mouse was clicked at: x=%d, y=%d\n", aMouseEvent->point.x, aMouseEvent->
if (NS_SUCCEEDED(result))
{
mSelectingTableCells = PR_TRUE;
mSelectingTableCellMode = PR_TRUE;//only shut off on normal mouse click.
mSelectingTableCellMode = TABLESELECTION_CELL;//only shut off on mouse up
mStartSelectedCell = selectedContent;
mEndSelectedCell = selectedContent;
@ -2190,7 +2253,6 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
{
if (!aEndCell) return NS_ERROR_NULL_POINTER;
mEndSelectedCell = aEndCell;
nsCOMPtr<nsIDOMNode> cellNode;
nsCOMPtr<nsIDOMRange> range;
@ -2215,12 +2277,7 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
// Get TableLayout interface to access cell data based on cellmap location
// frames are not ref counted, so don't use an nsCOMPtr
nsIFrame *frame = nsnull;
result = GetTracker()->GetPrimaryFrameFor(table, &frame);
if (!frame) return NS_ERROR_FAILURE;
nsITableLayout *tableLayoutObject = nsnull;
result = frame->QueryInterface(NS_GET_IID(nsITableLayout), (void**)(&tableLayoutObject));
if (NS_FAILED(result)) return result;
nsITableLayout *tableLayoutObject = GetTableLayout(table);
if (!tableLayoutObject) return NS_ERROR_FAILURE;
// Remove selected cells outside of new block limits
@ -2250,7 +2307,7 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
if (NS_FAILED(result)) return result;
}
nsCOMPtr<nsIDOMElement> cell;
nsCOMPtr<nsIDOMElement> cellElement;
PRInt32 rowSpan, colSpan, actualRowSpan, actualColSpan;
PRBool isSelected;
@ -2262,7 +2319,7 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
PRInt32 col = startColIndex;
while(PR_TRUE)
{
result = tableLayoutObject->GetCellDataAt(row, col, *getter_AddRefs(cell),
result = tableLayoutObject->GetCellDataAt(row, col, *getter_AddRefs(cellElement),
curRowIndex, curColIndex, rowSpan, colSpan,
actualRowSpan, actualColSpan, isSelected);
if (NS_FAILED(result)) return result;
@ -2270,21 +2327,9 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
NS_ASSERTION(actualColSpan, "!actualColSpan is 0!");
// Skip cells that are spanned from previous locations or are already selected
if (!isSelected && cell && row == curRowIndex && col == curColIndex)
if (!isSelected && cellElement && row == curRowIndex && col == curColIndex)
{
cellNode = do_QueryInterface(cell);
nsCOMPtr<nsIDOMNode> parent;
result = cellNode->GetParentNode(getter_AddRefs(parent));
if (NS_FAILED(result)) return result;
if (!parent) return NS_ERROR_FAILURE;
// Get child offset
nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parent);
nsCOMPtr<nsIContent> cellContent = do_QueryInterface(cell);
PRInt32 offset;
result = parentContent->IndexOf(cellContent, offset);
if (NS_FAILED(result)) return result;
result = CreateAndAddRange(parent, offset);
result = SelectCellElement(cellElement);
if (NS_FAILED(result)) return result;
}
// Done when we reach end column
@ -2306,6 +2351,114 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
return result;
}
nsresult
nsSelection::SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget)
{
if (!aCellContent) return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIContent> table;
nsresult result = GetParentTable(aCellContent, getter_AddRefs(table));
if (NS_FAILED(result)) return PR_FALSE;
if (!table) return NS_ERROR_NULL_POINTER;
// Get table and cell layout interfaces to access
// cell data based on cellmap location
// Frames are not ref counted, so don't use an nsCOMPtr
nsITableLayout *tableLayout = GetTableLayout(table);
if (!tableLayout) return NS_ERROR_FAILURE;
nsITableCellLayout *cellLayout = GetCellLayout(aCellContent);
if (!cellLayout) return NS_ERROR_FAILURE;
// Get location of target cell:
PRInt32 rowIndex, colIndex, curRowIndex, curColIndex;
result = cellLayout->GetCellIndexes(rowIndex, colIndex);
if (NS_FAILED(result)) return result;
// Be sure we start at proper beginning
// (This allows us to select row or col given ANY cell!)
if (aTarget == TABLESELECTION_ROW)
colIndex = 0;
if (aTarget == TABLESELECTION_COLUMN)
rowIndex = 0;
nsCOMPtr<nsIDOMElement> cellElement;
nsCOMPtr<nsIDOMElement> firstCell;
nsCOMPtr<nsIDOMElement> lastCell;
PRInt32 rowSpan, colSpan, actualRowSpan, actualColSpan;
PRBool isSelected;
do {
// Loop through all cells in column or row to find first and last
result = tableLayout->GetCellDataAt(rowIndex, colIndex, *getter_AddRefs(cellElement),
curRowIndex, curColIndex, rowSpan, colSpan,
actualRowSpan, actualColSpan, isSelected);
if (NS_FAILED(result)) return result;
if (cellElement)
{
if (!firstCell)
firstCell = cellElement;
lastCell = cellElement;
// Move to next cell in cellmap, skipping spanned locations
if (aTarget == TABLESELECTION_ROW)
colIndex += actualColSpan;
else
rowIndex += actualRowSpan;
}
}
while (cellElement);
// Use SelectBlockOfCells:
// This will replace existing selection,
// but allow unselecting by dragging out of selected region
if (firstCell && lastCell)
{
if (!mStartSelectedCell)
{
// We are starting a new block, so select the first cell
result = SelectCellElement(firstCell);
if (NS_FAILED(result)) return result;
}
nsCOMPtr<nsIContent> lastCellContent = do_QueryInterface(lastCell);
return SelectBlockOfCells(lastCellContent);
}
#if 0
// This is a more efficient strategy that appends row to current selection,
// but doesn't allow dragging OFF of an existing selection to unselect!
do {
// Loop through all cells in column or row
result = tableLayout->GetCellDataAt(rowIndex, colIndex, *getter_AddRefs(cellElement),
curRowIndex, curColIndex, rowSpan, colSpan,
actualRowSpan, actualColSpan, isSelected);
if (NS_FAILED(result)) return result;
// We're done when cell is not found
if (!cellElement) break;
// Check spans else we infinitely loop
NS_ASSERTION(actualColSpan, "actualColSpan is 0!");
NS_ASSERTION(actualRowSpan, "actualRowSpan is 0!");
// Skip cells that are already selected or span from outside our region
if (!isSelected && rowIndex == curRowIndex && colIndex == curColIndex)
{
result = SelectCellElement(cellElement);
if (NS_FAILED(result)) return result;
}
// Move to next row or column in cellmap, skipping spanned locations
if (aTarget == TABLESELECTION_ROW)
colIndex += actualColSpan;
else
rowIndex += actualRowSpan;
}
while (cellElement);
#endif
return NS_OK;
}
nsresult
nsSelection::GetFirstCellNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aCellNode)
{
@ -2420,13 +2573,7 @@ nsSelection::GetCellIndexes(nsIContent *aCell, PRInt32 &aRowIndex, PRInt32 &aCol
aColIndex=0; // initialize out params
aRowIndex=0;
nsIFrame *frame = nsnull; // frames are not ref counted, so don't use an nsCOMPtr
nsresult result = GetTracker()->GetPrimaryFrameFor(aCell, &frame);
if (!frame) return NS_ERROR_FAILURE;
nsITableCellLayout *cellLayoutObject=nsnull; // again, frames are not ref-counted
result = frame->QueryInterface(NS_GET_IID(nsITableCellLayout), (void**)(&cellLayoutObject));
if (NS_FAILED(result)) return result;
nsITableCellLayout *cellLayoutObject = GetCellLayout(aCell);
if (!cellLayoutObject) return NS_ERROR_FAILURE;
return cellLayoutObject->GetCellIndexes(aRowIndex, aColIndex);
}
@ -2486,6 +2633,24 @@ nsSelection::GetParentTable(nsIContent *aCell, nsIContent **aTable)
return result;
}
nsresult
nsSelection::SelectCellElement(nsIDOMElement *aCellElement)
{
nsCOMPtr<nsIDOMNode> cellNode = do_QueryInterface(aCellElement);
nsCOMPtr<nsIDOMNode> parent;
nsresult result = cellNode->GetParentNode(getter_AddRefs(parent));
if (NS_FAILED(result)) return result;
if (!parent) return NS_ERROR_FAILURE;
// Get child offset
nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parent);
nsCOMPtr<nsIContent> cellContent = do_QueryInterface(aCellElement);
PRInt32 offset;
result = parentContent->IndexOf(cellContent, offset);
if (NS_FAILED(result)) return result;
return CreateAndAddRange(parent, offset);
}
nsresult
nsSelection::CreateAndAddRange(nsIDOMNode *aParentNode, PRInt32 aOffset)
{

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

@ -74,7 +74,7 @@
#include "nsISelectionController.h"//for the enums
#define STATUS_CHECK_RETURN_MACRO() {if (!mTracker) return NS_ERROR_FAILURE;}
//#define DEBUG_TABLE 1
#define DEBUG_TABLE 1
//static NS_DEFINE_IID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
static NS_DEFINE_IID(kCGenContentIteratorCID, NS_GENERATEDCONTENTITERATOR_CID);
@ -277,7 +277,7 @@ public:
NS_IMETHOD SetMouseDownState(PRBool aState);
NS_IMETHOD GetMouseDownState(PRBool *aState);
NS_IMETHOD GetTableCellSelection(PRBool *aState){if (aState){*aState = mSelectingTableCellMode; return NS_OK;}return NS_ERROR_NULL_POINTER;}
NS_IMETHOD GetTableCellSelection(PRBool *aState){if (aState){*aState = mSelectingTableCellMode != 0; return NS_OK;}return NS_ERROR_NULL_POINTER;}
NS_IMETHOD GetTableCellSelectionStyleColor(const nsStyleColor **aStyleColor);
NS_IMETHOD GetSelection(SelectionType aType, nsIDOMSelection **aDomSelection);
@ -336,23 +336,28 @@ private:
nsDOMSelection *mDomSelections[nsISelectionController::NUM_SELECTIONTYPES];
nsIScrollableView *GetScrollView(){return mScrollView;}
// Table selection support. Unfortunately, we can't get at nsITableCellLayout
// and nsITableLayout through nsIFrame, so we have to duplicate editor code here
// Table selection support.
// Interfaces that let us get info based on cellmap locations
nsITableLayout* GetTableLayout(nsIContent *aTableContent);
nsITableCellLayout* GetCellLayout(nsIContent *aCellContent);
nsresult SelectBlockOfCells(nsIContent *aEndNode);
nsresult SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget);
nsresult GetCellIndexes(nsIContent *aCell, PRInt32 &aRowIndex, PRInt32 &aColIndex);
nsresult GetFirstSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange);
nsresult GetNextSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange);
nsresult GetFirstCellNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aCellNode);
// aTableNode may be null if table isn't needed to be returned
PRBool IsInSameTable(nsIContent *aContent1, nsIContent *aContent2, nsIContent **aTableNode);
nsresult GetParentTable(nsIContent *aCellNode, nsIContent **aTableNode);
nsresult SelectCellElement(nsIDOMElement* aCellElement);
nsresult CreateAndAddRange(nsIDOMNode *aParentNode, PRInt32 aOffset);
nsCOMPtr<nsIContent> mStartSelectedCell;
nsCOMPtr<nsIContent> mEndSelectedCell;
PRBool mSelectingTableCells;
PRBool mSelectingTableCellMode;
PRInt32 mSelectedCellIndex;
PRBool mSelectingTableCells;
PRUint32 mSelectingTableCellMode;
PRInt32 mSelectedCellIndex;
//batching
PRInt32 mBatching;
@ -781,7 +786,7 @@ nsSelection::nsSelection()
mHint = HINTLEFT;
sInstanceCount ++;
mSelectingTableCells = PR_FALSE;
mSelectingTableCellMode = PR_FALSE;
mSelectingTableCellMode = 0;
mSelectedCellIndex = 0;
//AUTO COPY REGISTRATION
@ -1537,7 +1542,7 @@ nsSelection::HandleClick(nsIContent *aNewFocus, PRUint32 aContentOffset,
// Don't take focus when dragging off of a table
if (!mSelectingTableCells)
{
mSelectingTableCellMode = PR_FALSE;
mSelectingTableCellMode = 0;
return TakeFocus(aNewFocus, aContentOffset, aContentEndOffset, aContinueSelection, aMultipleSelection);
}
@ -1994,6 +1999,32 @@ static PRBool IsCell(nsIContent *aContent)
return (tag != 0 && tag == nsSelection::sCellAtom);
}
nsITableCellLayout*
nsSelection::GetCellLayout(nsIContent *aCellContent)
{
// Get frame for cell
nsIFrame *cellFrame = nsnull;
nsresult result = GetTracker()->GetPrimaryFrameFor(aCellContent, &cellFrame);
if (!cellFrame) return nsnull;
nsITableCellLayout *cellLayoutObject = nsnull;
result = cellFrame->QueryInterface(NS_GET_IID(nsITableCellLayout), (void**)(&cellLayoutObject));
if (NS_FAILED(result)) return nsnull;
return cellLayoutObject;
}
nsITableLayout*
nsSelection::GetTableLayout(nsIContent *aTableContent)
{
// Get frame for table
nsIFrame *tableFrame = nsnull;
nsresult result = GetTracker()->GetPrimaryFrameFor(aTableContent, &tableFrame);
if (!tableFrame) return nsnull;
nsITableLayout *tableLayoutObject = nsnull;
result = tableFrame->QueryInterface(NS_GET_IID(nsITableLayout), (void**)(&tableLayoutObject));
if (NS_FAILED(result)) return nsnull;
return tableLayoutObject;
}
nsresult
nsSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOffset, PRUint32 aTarget, nsMouseEvent *aMouseEvent)
{
@ -2016,52 +2047,60 @@ nsSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOf
nsCOMPtr<nsIDOMNode> selectedNode = do_QueryInterface(selectedContent);
if (!selectedNode) return NS_ERROR_FAILURE;
// Stack-class to wrap all table selection changes in
// BeginBatchChanges() / EndBatchChanges()
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
nsSelectionBatcher selectionBatcher(mDomSelections[index]);
// When doing table selection, always set the direction to next
// so we can be sure that anchorNode's offset always points to the selected cell
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
mDomSelections[index]->SetDirection(eDirNext);
// Stack-class to wrap all table selection changes in
// BeginBatchChanges() / EndBatchChanges()
nsSelectionBatcher selectionBatcher(mDomSelections[index]);
if (mSelectingTableCells)
{
#ifdef DEBUG_TABLE
printf("HandleTableSelection: dragging mouse\n");
#endif
// We are drag-selecting
if (aTarget == TABLESELECTION_CELL)
if (aTarget != TABLESELECTION_TABLE)
{
// aTarget can be any "cell mode",
// so we can easily drag-select rows and columns
// Once user gets the col or row hit area onclick,
// they can drift into any cell to stay in col/row mode
// (aTarget will be TABLESELECTION_CELL)
// If dragging in the same cell as last event, do nothing
if (mEndSelectedCell == selectedContent)
return NS_OK;
if (mSelectingTableCellMode == TABLESELECTION_ROW ||
mSelectingTableCellMode == TABLESELECTION_COLUMN)
{
#ifdef DEBUG_TABLE
printf("HandleTableSelection: Dragged into a new cell\n");
printf("HandleTableSelection: Dragged into a new column or row\n");
#endif
// Reselect block of cells to new end location
return SelectBlockOfCells(selectedContent);
// Append anthor row or columns to the selection
return SelectRowOrColumn(selectedContent, mSelectingTableCellMode);
}
else if (mSelectingTableCellMode == TABLESELECTION_CELL)
{
#ifdef DEBUG_TABLE
printf("HandleTableSelection: Dragged into a new cell\n");
#endif
// Reselect block of cells to new end location
return SelectBlockOfCells(selectedContent);
}
}
// Do nothing if dragging in table, but outside a cell
return NS_OK;
}
else
{
// Not dragging -- mouse event is a click
#ifdef DEBUG_TABLE
printf("HandleTableSelection: CLICK event\n");
#endif
if (aTarget == TABLESELECTION_TABLE)
{
//TODO: Currently selects when clicked between cells,
// should we restrict to only around border?
// *** How do we get location data for cell and click?
mSelectingTableCells = PR_FALSE;
mStartSelectedCell = nsnull;
mEndSelectedCell = nsnull;
#ifdef DEBUG_cmanske
#ifdef DEBUG_TABLE
//#ifdef DEBUG_TABLE
printf("HandleTableSelection: CLICK event\n");
printf("HandleTableSelection: Selecting Table\n");
{
nsIFrame *frame = nsnull;
@ -2070,15 +2109,42 @@ printf("HandleTableSelection: Selecting Table\n");
{
nsRect rect1;
frame->GetRect(rect1);
printf("Table frame orgin: x=%d, y=%d\n", rect1.x, rect1.y);
printf("Frame rect: x=%d, y=%d, right=%d, bottom=%d\n", rect1.x, rect1.y, rect1.XMost(), rect1.YMost());
nsRect rect2 = rect1;
mDomSelections[index]->GetFrameToRootViewOffset(frame, &rect2.x, &rect2.y);
printf("Translated frame orgin: x=%d, y=%d\n", rect2.x, rect2.y);
printf("Mouse was clicked at: x=%d, y=%d\n", aMouseEvent->point.x, aMouseEvent->point.y);
printf("*** Widget-relative (refPoint): x=%d, y=%d\n", aMouseEvent->refPoint.x, aMouseEvent->refPoint.y);
}
/*
Useful frame methods:
GetRect(nsRect& aRect)
GetSize(nsSize& aSize)
*/
}
//#endif
#endif
#endif
if (aTarget == TABLESELECTION_ROW || aTarget == TABLESELECTION_COLUMN)
{
// Start drag-selecting mode so multiple rows/cols can be selected
mSelectingTableCells = PR_TRUE;
mSelectingTableCellMode = aTarget; //only shut off on mouse up.
// Force new selection block
mStartSelectedCell = nsnull;
mDomSelections[index]->ClearSelection();
return SelectRowOrColumn(selectedContent, aTarget);
}
else if (aTarget == TABLESELECTION_TABLE)
{
//TODO: We currently select entire table when clicked between cells,
// should we restrict to only around border?
// *** How do we get location data for cell and click?
mSelectingTableCells = PR_FALSE;
mStartSelectedCell = nsnull;
mEndSelectedCell = nsnull;
// Select the table
mDomSelections[index]->ClearSelection();
return CreateAndAddRange(parentNode, aContentOffset);
@ -2117,10 +2183,7 @@ printf("Mouse was clicked at: x=%d, y=%d\n", aMouseEvent->point.x, aMouseEvent->
result = parentContent->ChildAt(offset, *getter_AddRefs(childContent));
if (NS_FAILED(result)) return result;
if (childContent && IsCell(childContent))
{
previousCellParent = parent;
}
// We're done if we didn't find parent of a previously-selected cell
if (!previousCellParent) break;
@ -2176,7 +2239,7 @@ printf("Mouse was clicked at: x=%d, y=%d\n", aMouseEvent->point.x, aMouseEvent->
if (NS_SUCCEEDED(result))
{
mSelectingTableCells = PR_TRUE;
mSelectingTableCellMode = PR_TRUE;//only shut off on normal mouse click.
mSelectingTableCellMode = TABLESELECTION_CELL;//only shut off on mouse up
mStartSelectedCell = selectedContent;
mEndSelectedCell = selectedContent;
@ -2190,7 +2253,6 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
{
if (!aEndCell) return NS_ERROR_NULL_POINTER;
mEndSelectedCell = aEndCell;
nsCOMPtr<nsIDOMNode> cellNode;
nsCOMPtr<nsIDOMRange> range;
@ -2215,12 +2277,7 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
// Get TableLayout interface to access cell data based on cellmap location
// frames are not ref counted, so don't use an nsCOMPtr
nsIFrame *frame = nsnull;
result = GetTracker()->GetPrimaryFrameFor(table, &frame);
if (!frame) return NS_ERROR_FAILURE;
nsITableLayout *tableLayoutObject = nsnull;
result = frame->QueryInterface(NS_GET_IID(nsITableLayout), (void**)(&tableLayoutObject));
if (NS_FAILED(result)) return result;
nsITableLayout *tableLayoutObject = GetTableLayout(table);
if (!tableLayoutObject) return NS_ERROR_FAILURE;
// Remove selected cells outside of new block limits
@ -2250,7 +2307,7 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
if (NS_FAILED(result)) return result;
}
nsCOMPtr<nsIDOMElement> cell;
nsCOMPtr<nsIDOMElement> cellElement;
PRInt32 rowSpan, colSpan, actualRowSpan, actualColSpan;
PRBool isSelected;
@ -2262,7 +2319,7 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
PRInt32 col = startColIndex;
while(PR_TRUE)
{
result = tableLayoutObject->GetCellDataAt(row, col, *getter_AddRefs(cell),
result = tableLayoutObject->GetCellDataAt(row, col, *getter_AddRefs(cellElement),
curRowIndex, curColIndex, rowSpan, colSpan,
actualRowSpan, actualColSpan, isSelected);
if (NS_FAILED(result)) return result;
@ -2270,21 +2327,9 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
NS_ASSERTION(actualColSpan, "!actualColSpan is 0!");
// Skip cells that are spanned from previous locations or are already selected
if (!isSelected && cell && row == curRowIndex && col == curColIndex)
if (!isSelected && cellElement && row == curRowIndex && col == curColIndex)
{
cellNode = do_QueryInterface(cell);
nsCOMPtr<nsIDOMNode> parent;
result = cellNode->GetParentNode(getter_AddRefs(parent));
if (NS_FAILED(result)) return result;
if (!parent) return NS_ERROR_FAILURE;
// Get child offset
nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parent);
nsCOMPtr<nsIContent> cellContent = do_QueryInterface(cell);
PRInt32 offset;
result = parentContent->IndexOf(cellContent, offset);
if (NS_FAILED(result)) return result;
result = CreateAndAddRange(parent, offset);
result = SelectCellElement(cellElement);
if (NS_FAILED(result)) return result;
}
// Done when we reach end column
@ -2306,6 +2351,114 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
return result;
}
nsresult
nsSelection::SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget)
{
if (!aCellContent) return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIContent> table;
nsresult result = GetParentTable(aCellContent, getter_AddRefs(table));
if (NS_FAILED(result)) return PR_FALSE;
if (!table) return NS_ERROR_NULL_POINTER;
// Get table and cell layout interfaces to access
// cell data based on cellmap location
// Frames are not ref counted, so don't use an nsCOMPtr
nsITableLayout *tableLayout = GetTableLayout(table);
if (!tableLayout) return NS_ERROR_FAILURE;
nsITableCellLayout *cellLayout = GetCellLayout(aCellContent);
if (!cellLayout) return NS_ERROR_FAILURE;
// Get location of target cell:
PRInt32 rowIndex, colIndex, curRowIndex, curColIndex;
result = cellLayout->GetCellIndexes(rowIndex, colIndex);
if (NS_FAILED(result)) return result;
// Be sure we start at proper beginning
// (This allows us to select row or col given ANY cell!)
if (aTarget == TABLESELECTION_ROW)
colIndex = 0;
if (aTarget == TABLESELECTION_COLUMN)
rowIndex = 0;
nsCOMPtr<nsIDOMElement> cellElement;
nsCOMPtr<nsIDOMElement> firstCell;
nsCOMPtr<nsIDOMElement> lastCell;
PRInt32 rowSpan, colSpan, actualRowSpan, actualColSpan;
PRBool isSelected;
do {
// Loop through all cells in column or row to find first and last
result = tableLayout->GetCellDataAt(rowIndex, colIndex, *getter_AddRefs(cellElement),
curRowIndex, curColIndex, rowSpan, colSpan,
actualRowSpan, actualColSpan, isSelected);
if (NS_FAILED(result)) return result;
if (cellElement)
{
if (!firstCell)
firstCell = cellElement;
lastCell = cellElement;
// Move to next cell in cellmap, skipping spanned locations
if (aTarget == TABLESELECTION_ROW)
colIndex += actualColSpan;
else
rowIndex += actualRowSpan;
}
}
while (cellElement);
// Use SelectBlockOfCells:
// This will replace existing selection,
// but allow unselecting by dragging out of selected region
if (firstCell && lastCell)
{
if (!mStartSelectedCell)
{
// We are starting a new block, so select the first cell
result = SelectCellElement(firstCell);
if (NS_FAILED(result)) return result;
}
nsCOMPtr<nsIContent> lastCellContent = do_QueryInterface(lastCell);
return SelectBlockOfCells(lastCellContent);
}
#if 0
// This is a more efficient strategy that appends row to current selection,
// but doesn't allow dragging OFF of an existing selection to unselect!
do {
// Loop through all cells in column or row
result = tableLayout->GetCellDataAt(rowIndex, colIndex, *getter_AddRefs(cellElement),
curRowIndex, curColIndex, rowSpan, colSpan,
actualRowSpan, actualColSpan, isSelected);
if (NS_FAILED(result)) return result;
// We're done when cell is not found
if (!cellElement) break;
// Check spans else we infinitely loop
NS_ASSERTION(actualColSpan, "actualColSpan is 0!");
NS_ASSERTION(actualRowSpan, "actualRowSpan is 0!");
// Skip cells that are already selected or span from outside our region
if (!isSelected && rowIndex == curRowIndex && colIndex == curColIndex)
{
result = SelectCellElement(cellElement);
if (NS_FAILED(result)) return result;
}
// Move to next row or column in cellmap, skipping spanned locations
if (aTarget == TABLESELECTION_ROW)
colIndex += actualColSpan;
else
rowIndex += actualRowSpan;
}
while (cellElement);
#endif
return NS_OK;
}
nsresult
nsSelection::GetFirstCellNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aCellNode)
{
@ -2420,13 +2573,7 @@ nsSelection::GetCellIndexes(nsIContent *aCell, PRInt32 &aRowIndex, PRInt32 &aCol
aColIndex=0; // initialize out params
aRowIndex=0;
nsIFrame *frame = nsnull; // frames are not ref counted, so don't use an nsCOMPtr
nsresult result = GetTracker()->GetPrimaryFrameFor(aCell, &frame);
if (!frame) return NS_ERROR_FAILURE;
nsITableCellLayout *cellLayoutObject=nsnull; // again, frames are not ref-counted
result = frame->QueryInterface(NS_GET_IID(nsITableCellLayout), (void**)(&cellLayoutObject));
if (NS_FAILED(result)) return result;
nsITableCellLayout *cellLayoutObject = GetCellLayout(aCell);
if (!cellLayoutObject) return NS_ERROR_FAILURE;
return cellLayoutObject->GetCellIndexes(aRowIndex, aColIndex);
}
@ -2486,6 +2633,24 @@ nsSelection::GetParentTable(nsIContent *aCell, nsIContent **aTable)
return result;
}
nsresult
nsSelection::SelectCellElement(nsIDOMElement *aCellElement)
{
nsCOMPtr<nsIDOMNode> cellNode = do_QueryInterface(aCellElement);
nsCOMPtr<nsIDOMNode> parent;
nsresult result = cellNode->GetParentNode(getter_AddRefs(parent));
if (NS_FAILED(result)) return result;
if (!parent) return NS_ERROR_FAILURE;
// Get child offset
nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parent);
nsCOMPtr<nsIContent> cellContent = do_QueryInterface(aCellElement);
PRInt32 offset;
result = parentContent->IndexOf(cellContent, offset);
if (NS_FAILED(result)) return result;
return CreateAndAddRange(parent, offset);
}
nsresult
nsSelection::CreateAndAddRange(nsIDOMNode *aParentNode, PRInt32 aOffset)
{

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

@ -866,6 +866,8 @@ nsFrame::GetDataForTableSelection(nsMouseEvent *aMouseEvent, nsIContent **aParen
nsresult result = NS_OK;
PRBool foundCell = PR_FALSE;
PRBool foundTable = PR_FALSE;
PRBool selectColumn = PR_FALSE;
PRBool selectRow = PR_FALSE;
while (frame && NS_SUCCEEDED(result))
{
@ -875,6 +877,23 @@ nsFrame::GetDataForTableSelection(nsMouseEvent *aMouseEvent, nsIContent **aParen
if (NS_SUCCEEDED(result) && cellElement)
{
foundCell = PR_TRUE;
PRInt32 colIndex, rowIndex;
result = cellElement->GetCellIndexes(rowIndex, colIndex);
// Give precedence to row over column
if (colIndex == 0)
{
//printf(" * SelRow: x=%d, y=%d\n", aMouseEvent->point.x, aMouseEvent->point.y);
//TODO: We need to test for proximity to top border
// For now, just go into row selection mode
selectRow = PR_TRUE;
}
else if (rowIndex == 0)
{
//printf(" * SelCol: x=%d, y=%d\n", aMouseEvent->point.x, aMouseEvent->point.y);
//TODO: We need to test for proximity to left border
// For now, just go into COLUMN selection mode
selectColumn = PR_TRUE;
}
break;
}
else
@ -887,6 +906,8 @@ nsFrame::GetDataForTableSelection(nsMouseEvent *aMouseEvent, nsIContent **aParen
if (NS_SUCCEEDED(result) && tableElement)
{
foundTable = PR_TRUE;
//TODO: How can we select row when along left table edge
// or select column when along top edge?
break;
} else {
result = frame->GetParent(&frame);
@ -918,14 +939,14 @@ nsFrame::GetDataForTableSelection(nsMouseEvent *aMouseEvent, nsIContent **aParen
*aContentOffset = offset;
if (foundCell)
if (selectRow)
*aTarget = TABLESELECTION_ROW;
else if (selectColumn)
*aTarget = TABLESELECTION_COLUMN;
else if (foundCell)
*aTarget = TABLESELECTION_CELL;
if (foundTable)
{
//TODO: Put logic to find "hit" spots for selecting column or row here
else if (foundTable)
*aTarget = TABLESELECTION_TABLE;
}
return NS_OK;
}

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

@ -74,7 +74,7 @@
#include "nsISelectionController.h"//for the enums
#define STATUS_CHECK_RETURN_MACRO() {if (!mTracker) return NS_ERROR_FAILURE;}
//#define DEBUG_TABLE 1
#define DEBUG_TABLE 1
//static NS_DEFINE_IID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
static NS_DEFINE_IID(kCGenContentIteratorCID, NS_GENERATEDCONTENTITERATOR_CID);
@ -277,7 +277,7 @@ public:
NS_IMETHOD SetMouseDownState(PRBool aState);
NS_IMETHOD GetMouseDownState(PRBool *aState);
NS_IMETHOD GetTableCellSelection(PRBool *aState){if (aState){*aState = mSelectingTableCellMode; return NS_OK;}return NS_ERROR_NULL_POINTER;}
NS_IMETHOD GetTableCellSelection(PRBool *aState){if (aState){*aState = mSelectingTableCellMode != 0; return NS_OK;}return NS_ERROR_NULL_POINTER;}
NS_IMETHOD GetTableCellSelectionStyleColor(const nsStyleColor **aStyleColor);
NS_IMETHOD GetSelection(SelectionType aType, nsIDOMSelection **aDomSelection);
@ -336,23 +336,28 @@ private:
nsDOMSelection *mDomSelections[nsISelectionController::NUM_SELECTIONTYPES];
nsIScrollableView *GetScrollView(){return mScrollView;}
// Table selection support. Unfortunately, we can't get at nsITableCellLayout
// and nsITableLayout through nsIFrame, so we have to duplicate editor code here
// Table selection support.
// Interfaces that let us get info based on cellmap locations
nsITableLayout* GetTableLayout(nsIContent *aTableContent);
nsITableCellLayout* GetCellLayout(nsIContent *aCellContent);
nsresult SelectBlockOfCells(nsIContent *aEndNode);
nsresult SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget);
nsresult GetCellIndexes(nsIContent *aCell, PRInt32 &aRowIndex, PRInt32 &aColIndex);
nsresult GetFirstSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange);
nsresult GetNextSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange);
nsresult GetFirstCellNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aCellNode);
// aTableNode may be null if table isn't needed to be returned
PRBool IsInSameTable(nsIContent *aContent1, nsIContent *aContent2, nsIContent **aTableNode);
nsresult GetParentTable(nsIContent *aCellNode, nsIContent **aTableNode);
nsresult SelectCellElement(nsIDOMElement* aCellElement);
nsresult CreateAndAddRange(nsIDOMNode *aParentNode, PRInt32 aOffset);
nsCOMPtr<nsIContent> mStartSelectedCell;
nsCOMPtr<nsIContent> mEndSelectedCell;
PRBool mSelectingTableCells;
PRBool mSelectingTableCellMode;
PRInt32 mSelectedCellIndex;
PRBool mSelectingTableCells;
PRUint32 mSelectingTableCellMode;
PRInt32 mSelectedCellIndex;
//batching
PRInt32 mBatching;
@ -781,7 +786,7 @@ nsSelection::nsSelection()
mHint = HINTLEFT;
sInstanceCount ++;
mSelectingTableCells = PR_FALSE;
mSelectingTableCellMode = PR_FALSE;
mSelectingTableCellMode = 0;
mSelectedCellIndex = 0;
//AUTO COPY REGISTRATION
@ -1537,7 +1542,7 @@ nsSelection::HandleClick(nsIContent *aNewFocus, PRUint32 aContentOffset,
// Don't take focus when dragging off of a table
if (!mSelectingTableCells)
{
mSelectingTableCellMode = PR_FALSE;
mSelectingTableCellMode = 0;
return TakeFocus(aNewFocus, aContentOffset, aContentEndOffset, aContinueSelection, aMultipleSelection);
}
@ -1994,6 +1999,32 @@ static PRBool IsCell(nsIContent *aContent)
return (tag != 0 && tag == nsSelection::sCellAtom);
}
nsITableCellLayout*
nsSelection::GetCellLayout(nsIContent *aCellContent)
{
// Get frame for cell
nsIFrame *cellFrame = nsnull;
nsresult result = GetTracker()->GetPrimaryFrameFor(aCellContent, &cellFrame);
if (!cellFrame) return nsnull;
nsITableCellLayout *cellLayoutObject = nsnull;
result = cellFrame->QueryInterface(NS_GET_IID(nsITableCellLayout), (void**)(&cellLayoutObject));
if (NS_FAILED(result)) return nsnull;
return cellLayoutObject;
}
nsITableLayout*
nsSelection::GetTableLayout(nsIContent *aTableContent)
{
// Get frame for table
nsIFrame *tableFrame = nsnull;
nsresult result = GetTracker()->GetPrimaryFrameFor(aTableContent, &tableFrame);
if (!tableFrame) return nsnull;
nsITableLayout *tableLayoutObject = nsnull;
result = tableFrame->QueryInterface(NS_GET_IID(nsITableLayout), (void**)(&tableLayoutObject));
if (NS_FAILED(result)) return nsnull;
return tableLayoutObject;
}
nsresult
nsSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOffset, PRUint32 aTarget, nsMouseEvent *aMouseEvent)
{
@ -2016,52 +2047,60 @@ nsSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOf
nsCOMPtr<nsIDOMNode> selectedNode = do_QueryInterface(selectedContent);
if (!selectedNode) return NS_ERROR_FAILURE;
// Stack-class to wrap all table selection changes in
// BeginBatchChanges() / EndBatchChanges()
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
nsSelectionBatcher selectionBatcher(mDomSelections[index]);
// When doing table selection, always set the direction to next
// so we can be sure that anchorNode's offset always points to the selected cell
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
mDomSelections[index]->SetDirection(eDirNext);
// Stack-class to wrap all table selection changes in
// BeginBatchChanges() / EndBatchChanges()
nsSelectionBatcher selectionBatcher(mDomSelections[index]);
if (mSelectingTableCells)
{
#ifdef DEBUG_TABLE
printf("HandleTableSelection: dragging mouse\n");
#endif
// We are drag-selecting
if (aTarget == TABLESELECTION_CELL)
if (aTarget != TABLESELECTION_TABLE)
{
// aTarget can be any "cell mode",
// so we can easily drag-select rows and columns
// Once user gets the col or row hit area onclick,
// they can drift into any cell to stay in col/row mode
// (aTarget will be TABLESELECTION_CELL)
// If dragging in the same cell as last event, do nothing
if (mEndSelectedCell == selectedContent)
return NS_OK;
if (mSelectingTableCellMode == TABLESELECTION_ROW ||
mSelectingTableCellMode == TABLESELECTION_COLUMN)
{
#ifdef DEBUG_TABLE
printf("HandleTableSelection: Dragged into a new cell\n");
printf("HandleTableSelection: Dragged into a new column or row\n");
#endif
// Reselect block of cells to new end location
return SelectBlockOfCells(selectedContent);
// Append anthor row or columns to the selection
return SelectRowOrColumn(selectedContent, mSelectingTableCellMode);
}
else if (mSelectingTableCellMode == TABLESELECTION_CELL)
{
#ifdef DEBUG_TABLE
printf("HandleTableSelection: Dragged into a new cell\n");
#endif
// Reselect block of cells to new end location
return SelectBlockOfCells(selectedContent);
}
}
// Do nothing if dragging in table, but outside a cell
return NS_OK;
}
else
{
// Not dragging -- mouse event is a click
#ifdef DEBUG_TABLE
printf("HandleTableSelection: CLICK event\n");
#endif
if (aTarget == TABLESELECTION_TABLE)
{
//TODO: Currently selects when clicked between cells,
// should we restrict to only around border?
// *** How do we get location data for cell and click?
mSelectingTableCells = PR_FALSE;
mStartSelectedCell = nsnull;
mEndSelectedCell = nsnull;
#ifdef DEBUG_cmanske
#ifdef DEBUG_TABLE
//#ifdef DEBUG_TABLE
printf("HandleTableSelection: CLICK event\n");
printf("HandleTableSelection: Selecting Table\n");
{
nsIFrame *frame = nsnull;
@ -2070,15 +2109,42 @@ printf("HandleTableSelection: Selecting Table\n");
{
nsRect rect1;
frame->GetRect(rect1);
printf("Table frame orgin: x=%d, y=%d\n", rect1.x, rect1.y);
printf("Frame rect: x=%d, y=%d, right=%d, bottom=%d\n", rect1.x, rect1.y, rect1.XMost(), rect1.YMost());
nsRect rect2 = rect1;
mDomSelections[index]->GetFrameToRootViewOffset(frame, &rect2.x, &rect2.y);
printf("Translated frame orgin: x=%d, y=%d\n", rect2.x, rect2.y);
printf("Mouse was clicked at: x=%d, y=%d\n", aMouseEvent->point.x, aMouseEvent->point.y);
printf("*** Widget-relative (refPoint): x=%d, y=%d\n", aMouseEvent->refPoint.x, aMouseEvent->refPoint.y);
}
/*
Useful frame methods:
GetRect(nsRect& aRect)
GetSize(nsSize& aSize)
*/
}
//#endif
#endif
#endif
if (aTarget == TABLESELECTION_ROW || aTarget == TABLESELECTION_COLUMN)
{
// Start drag-selecting mode so multiple rows/cols can be selected
mSelectingTableCells = PR_TRUE;
mSelectingTableCellMode = aTarget; //only shut off on mouse up.
// Force new selection block
mStartSelectedCell = nsnull;
mDomSelections[index]->ClearSelection();
return SelectRowOrColumn(selectedContent, aTarget);
}
else if (aTarget == TABLESELECTION_TABLE)
{
//TODO: We currently select entire table when clicked between cells,
// should we restrict to only around border?
// *** How do we get location data for cell and click?
mSelectingTableCells = PR_FALSE;
mStartSelectedCell = nsnull;
mEndSelectedCell = nsnull;
// Select the table
mDomSelections[index]->ClearSelection();
return CreateAndAddRange(parentNode, aContentOffset);
@ -2117,10 +2183,7 @@ printf("Mouse was clicked at: x=%d, y=%d\n", aMouseEvent->point.x, aMouseEvent->
result = parentContent->ChildAt(offset, *getter_AddRefs(childContent));
if (NS_FAILED(result)) return result;
if (childContent && IsCell(childContent))
{
previousCellParent = parent;
}
// We're done if we didn't find parent of a previously-selected cell
if (!previousCellParent) break;
@ -2176,7 +2239,7 @@ printf("Mouse was clicked at: x=%d, y=%d\n", aMouseEvent->point.x, aMouseEvent->
if (NS_SUCCEEDED(result))
{
mSelectingTableCells = PR_TRUE;
mSelectingTableCellMode = PR_TRUE;//only shut off on normal mouse click.
mSelectingTableCellMode = TABLESELECTION_CELL;//only shut off on mouse up
mStartSelectedCell = selectedContent;
mEndSelectedCell = selectedContent;
@ -2190,7 +2253,6 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
{
if (!aEndCell) return NS_ERROR_NULL_POINTER;
mEndSelectedCell = aEndCell;
nsCOMPtr<nsIDOMNode> cellNode;
nsCOMPtr<nsIDOMRange> range;
@ -2215,12 +2277,7 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
// Get TableLayout interface to access cell data based on cellmap location
// frames are not ref counted, so don't use an nsCOMPtr
nsIFrame *frame = nsnull;
result = GetTracker()->GetPrimaryFrameFor(table, &frame);
if (!frame) return NS_ERROR_FAILURE;
nsITableLayout *tableLayoutObject = nsnull;
result = frame->QueryInterface(NS_GET_IID(nsITableLayout), (void**)(&tableLayoutObject));
if (NS_FAILED(result)) return result;
nsITableLayout *tableLayoutObject = GetTableLayout(table);
if (!tableLayoutObject) return NS_ERROR_FAILURE;
// Remove selected cells outside of new block limits
@ -2250,7 +2307,7 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
if (NS_FAILED(result)) return result;
}
nsCOMPtr<nsIDOMElement> cell;
nsCOMPtr<nsIDOMElement> cellElement;
PRInt32 rowSpan, colSpan, actualRowSpan, actualColSpan;
PRBool isSelected;
@ -2262,7 +2319,7 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
PRInt32 col = startColIndex;
while(PR_TRUE)
{
result = tableLayoutObject->GetCellDataAt(row, col, *getter_AddRefs(cell),
result = tableLayoutObject->GetCellDataAt(row, col, *getter_AddRefs(cellElement),
curRowIndex, curColIndex, rowSpan, colSpan,
actualRowSpan, actualColSpan, isSelected);
if (NS_FAILED(result)) return result;
@ -2270,21 +2327,9 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
NS_ASSERTION(actualColSpan, "!actualColSpan is 0!");
// Skip cells that are spanned from previous locations or are already selected
if (!isSelected && cell && row == curRowIndex && col == curColIndex)
if (!isSelected && cellElement && row == curRowIndex && col == curColIndex)
{
cellNode = do_QueryInterface(cell);
nsCOMPtr<nsIDOMNode> parent;
result = cellNode->GetParentNode(getter_AddRefs(parent));
if (NS_FAILED(result)) return result;
if (!parent) return NS_ERROR_FAILURE;
// Get child offset
nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parent);
nsCOMPtr<nsIContent> cellContent = do_QueryInterface(cell);
PRInt32 offset;
result = parentContent->IndexOf(cellContent, offset);
if (NS_FAILED(result)) return result;
result = CreateAndAddRange(parent, offset);
result = SelectCellElement(cellElement);
if (NS_FAILED(result)) return result;
}
// Done when we reach end column
@ -2306,6 +2351,114 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
return result;
}
nsresult
nsSelection::SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget)
{
if (!aCellContent) return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIContent> table;
nsresult result = GetParentTable(aCellContent, getter_AddRefs(table));
if (NS_FAILED(result)) return PR_FALSE;
if (!table) return NS_ERROR_NULL_POINTER;
// Get table and cell layout interfaces to access
// cell data based on cellmap location
// Frames are not ref counted, so don't use an nsCOMPtr
nsITableLayout *tableLayout = GetTableLayout(table);
if (!tableLayout) return NS_ERROR_FAILURE;
nsITableCellLayout *cellLayout = GetCellLayout(aCellContent);
if (!cellLayout) return NS_ERROR_FAILURE;
// Get location of target cell:
PRInt32 rowIndex, colIndex, curRowIndex, curColIndex;
result = cellLayout->GetCellIndexes(rowIndex, colIndex);
if (NS_FAILED(result)) return result;
// Be sure we start at proper beginning
// (This allows us to select row or col given ANY cell!)
if (aTarget == TABLESELECTION_ROW)
colIndex = 0;
if (aTarget == TABLESELECTION_COLUMN)
rowIndex = 0;
nsCOMPtr<nsIDOMElement> cellElement;
nsCOMPtr<nsIDOMElement> firstCell;
nsCOMPtr<nsIDOMElement> lastCell;
PRInt32 rowSpan, colSpan, actualRowSpan, actualColSpan;
PRBool isSelected;
do {
// Loop through all cells in column or row to find first and last
result = tableLayout->GetCellDataAt(rowIndex, colIndex, *getter_AddRefs(cellElement),
curRowIndex, curColIndex, rowSpan, colSpan,
actualRowSpan, actualColSpan, isSelected);
if (NS_FAILED(result)) return result;
if (cellElement)
{
if (!firstCell)
firstCell = cellElement;
lastCell = cellElement;
// Move to next cell in cellmap, skipping spanned locations
if (aTarget == TABLESELECTION_ROW)
colIndex += actualColSpan;
else
rowIndex += actualRowSpan;
}
}
while (cellElement);
// Use SelectBlockOfCells:
// This will replace existing selection,
// but allow unselecting by dragging out of selected region
if (firstCell && lastCell)
{
if (!mStartSelectedCell)
{
// We are starting a new block, so select the first cell
result = SelectCellElement(firstCell);
if (NS_FAILED(result)) return result;
}
nsCOMPtr<nsIContent> lastCellContent = do_QueryInterface(lastCell);
return SelectBlockOfCells(lastCellContent);
}
#if 0
// This is a more efficient strategy that appends row to current selection,
// but doesn't allow dragging OFF of an existing selection to unselect!
do {
// Loop through all cells in column or row
result = tableLayout->GetCellDataAt(rowIndex, colIndex, *getter_AddRefs(cellElement),
curRowIndex, curColIndex, rowSpan, colSpan,
actualRowSpan, actualColSpan, isSelected);
if (NS_FAILED(result)) return result;
// We're done when cell is not found
if (!cellElement) break;
// Check spans else we infinitely loop
NS_ASSERTION(actualColSpan, "actualColSpan is 0!");
NS_ASSERTION(actualRowSpan, "actualRowSpan is 0!");
// Skip cells that are already selected or span from outside our region
if (!isSelected && rowIndex == curRowIndex && colIndex == curColIndex)
{
result = SelectCellElement(cellElement);
if (NS_FAILED(result)) return result;
}
// Move to next row or column in cellmap, skipping spanned locations
if (aTarget == TABLESELECTION_ROW)
colIndex += actualColSpan;
else
rowIndex += actualRowSpan;
}
while (cellElement);
#endif
return NS_OK;
}
nsresult
nsSelection::GetFirstCellNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aCellNode)
{
@ -2420,13 +2573,7 @@ nsSelection::GetCellIndexes(nsIContent *aCell, PRInt32 &aRowIndex, PRInt32 &aCol
aColIndex=0; // initialize out params
aRowIndex=0;
nsIFrame *frame = nsnull; // frames are not ref counted, so don't use an nsCOMPtr
nsresult result = GetTracker()->GetPrimaryFrameFor(aCell, &frame);
if (!frame) return NS_ERROR_FAILURE;
nsITableCellLayout *cellLayoutObject=nsnull; // again, frames are not ref-counted
result = frame->QueryInterface(NS_GET_IID(nsITableCellLayout), (void**)(&cellLayoutObject));
if (NS_FAILED(result)) return result;
nsITableCellLayout *cellLayoutObject = GetCellLayout(aCell);
if (!cellLayoutObject) return NS_ERROR_FAILURE;
return cellLayoutObject->GetCellIndexes(aRowIndex, aColIndex);
}
@ -2486,6 +2633,24 @@ nsSelection::GetParentTable(nsIContent *aCell, nsIContent **aTable)
return result;
}
nsresult
nsSelection::SelectCellElement(nsIDOMElement *aCellElement)
{
nsCOMPtr<nsIDOMNode> cellNode = do_QueryInterface(aCellElement);
nsCOMPtr<nsIDOMNode> parent;
nsresult result = cellNode->GetParentNode(getter_AddRefs(parent));
if (NS_FAILED(result)) return result;
if (!parent) return NS_ERROR_FAILURE;
// Get child offset
nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parent);
nsCOMPtr<nsIContent> cellContent = do_QueryInterface(aCellElement);
PRInt32 offset;
result = parentContent->IndexOf(cellContent, offset);
if (NS_FAILED(result)) return result;
return CreateAndAddRange(parent, offset);
}
nsresult
nsSelection::CreateAndAddRange(nsIDOMNode *aParentNode, PRInt32 aOffset)
{

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

@ -866,6 +866,8 @@ nsFrame::GetDataForTableSelection(nsMouseEvent *aMouseEvent, nsIContent **aParen
nsresult result = NS_OK;
PRBool foundCell = PR_FALSE;
PRBool foundTable = PR_FALSE;
PRBool selectColumn = PR_FALSE;
PRBool selectRow = PR_FALSE;
while (frame && NS_SUCCEEDED(result))
{
@ -875,6 +877,23 @@ nsFrame::GetDataForTableSelection(nsMouseEvent *aMouseEvent, nsIContent **aParen
if (NS_SUCCEEDED(result) && cellElement)
{
foundCell = PR_TRUE;
PRInt32 colIndex, rowIndex;
result = cellElement->GetCellIndexes(rowIndex, colIndex);
// Give precedence to row over column
if (colIndex == 0)
{
//printf(" * SelRow: x=%d, y=%d\n", aMouseEvent->point.x, aMouseEvent->point.y);
//TODO: We need to test for proximity to top border
// For now, just go into row selection mode
selectRow = PR_TRUE;
}
else if (rowIndex == 0)
{
//printf(" * SelCol: x=%d, y=%d\n", aMouseEvent->point.x, aMouseEvent->point.y);
//TODO: We need to test for proximity to left border
// For now, just go into COLUMN selection mode
selectColumn = PR_TRUE;
}
break;
}
else
@ -887,6 +906,8 @@ nsFrame::GetDataForTableSelection(nsMouseEvent *aMouseEvent, nsIContent **aParen
if (NS_SUCCEEDED(result) && tableElement)
{
foundTable = PR_TRUE;
//TODO: How can we select row when along left table edge
// or select column when along top edge?
break;
} else {
result = frame->GetParent(&frame);
@ -918,14 +939,14 @@ nsFrame::GetDataForTableSelection(nsMouseEvent *aMouseEvent, nsIContent **aParen
*aContentOffset = offset;
if (foundCell)
if (selectRow)
*aTarget = TABLESELECTION_ROW;
else if (selectColumn)
*aTarget = TABLESELECTION_COLUMN;
else if (foundCell)
*aTarget = TABLESELECTION_CELL;
if (foundTable)
{
//TODO: Put logic to find "hit" spots for selecting column or row here
else if (foundTable)
*aTarget = TABLESELECTION_TABLE;
}
return NS_OK;
}