From 43fbb26cff8aa80890f4d39bee7394c3681aa400 Mon Sep 17 00:00:00 2001 From: "hyatt%netscape.com" Date: Wed, 28 Mar 2001 22:45:43 +0000 Subject: [PATCH] Fix for 73506. r=danm, sr=mscott --- .../outliner/public/nsIOutlinerBoxObject.idl | 6 +- .../src/outliner/src/nsOutlinerBodyFrame.cpp | 192 +++++++++++++++--- .../src/outliner/src/nsOutlinerBodyFrame.h | 6 +- .../src/outliner/src/nsOutlinerBoxObject.cpp | 5 +- .../base/src/tree/public/nsITreeBoxObject.idl | 6 +- .../xul/base/src/tree/src/nsTreeBodyFrame.cpp | 192 +++++++++++++++--- .../xul/base/src/tree/src/nsTreeBodyFrame.h | 6 +- .../xul/base/src/tree/src/nsTreeBoxObject.cpp | 5 +- .../resources/content/outlinerBindings.xml | 35 ++-- 9 files changed, 377 insertions(+), 76 deletions(-) diff --git a/layout/xul/base/src/outliner/public/nsIOutlinerBoxObject.idl b/layout/xul/base/src/outliner/public/nsIOutlinerBoxObject.idl index cf255c040987..bde4a5302602 100644 --- a/layout/xul/base/src/outliner/public/nsIOutlinerBoxObject.idl +++ b/layout/xul/base/src/outliner/public/nsIOutlinerBoxObject.idl @@ -78,8 +78,10 @@ interface nsIOutlinerBoxObject : nsISupports void invalidateRange(in long startIndex, in long endIndex); void invalidateScrollbar(); - // A hit test that can tell you what cell the mouse is over. - void getCellAt(in long x, in long y, out long row, out wstring colID); + // A hit test that can tell you what cell the mouse is over. Row is the row index + // hit. ColID is the column hit. ChildElt is the pseudoelement hit: this can have + // values of "cell", "twisty", "image", and "text". + void getCellAt(in long x, in long y, out long row, out wstring colID, out wstring childElt); // The view is responsible for calling these notification methods when // rows are added or removed. Index is the position at which the new diff --git a/layout/xul/base/src/outliner/src/nsOutlinerBodyFrame.cpp b/layout/xul/base/src/outliner/src/nsOutlinerBodyFrame.cpp index dc2897bed1e8..669cd16e8687 100644 --- a/layout/xul/base/src/outliner/src/nsOutlinerBodyFrame.cpp +++ b/layout/xul/base/src/outliner/src/nsOutlinerBodyFrame.cpp @@ -329,6 +329,16 @@ NS_IMETHODIMP nsOutlinerBodyFrame::Reflow(nsIPresContext* aPresContext, return NS_OK; } +static void +AdjustForBorderPadding(nsIStyleContext* aContext, nsRect& aRect) +{ + nsMargin m(0,0,0,0); + nsStyleBorderPadding bPad; + aContext->GetStyle(eStyleStruct_BorderPaddingShortcut, (nsStyleStruct&)bPad); + bPad.GetBorderPadding(m); + aRect.Deflate(m); +} + NS_IMETHODIMP nsOutlinerBodyFrame::GetView(nsIOutlinerView * *aView) { *aView = mView; @@ -547,7 +557,8 @@ NS_IMETHODIMP nsOutlinerBodyFrame::InvalidateScrollbar() return NS_OK; } -NS_IMETHODIMP nsOutlinerBodyFrame::GetCellAt(PRInt32 aX, PRInt32 aY, PRInt32* aRow, PRUnichar** aColID) +NS_IMETHODIMP nsOutlinerBodyFrame::GetCellAt(PRInt32 aX, PRInt32 aY, PRInt32* aRow, PRUnichar** aColID, + PRUnichar** aChildElt) { // Ensure we have a row height. if (mRowHeight == 0) @@ -591,19 +602,156 @@ NS_IMETHODIMP nsOutlinerBodyFrame::GetCellAt(PRInt32 aX, PRInt32 aY, PRInt32* aR nscoord currX = mInnerBox.x; for (nsOutlinerColumn* currCol = mColumns; currCol && currX < mInnerBox.x+mInnerBox.width; currCol = currCol->GetNext()) { - nsRect colRect(currX, mInnerBox.y, currCol->GetWidth(), mInnerBox.height); - PRInt32 overflow = colRect.x+colRect.width-(mInnerBox.x+mInnerBox.width); + nsRect cellRect(currX, mInnerBox.y+mRowHeight*(*aRow-mTopRowIndex), currCol->GetWidth(), mRowHeight); + PRInt32 overflow = cellRect.x+cellRect.width-(mInnerBox.x+mInnerBox.width); if (overflow > 0) - colRect.width -= overflow; + cellRect.width -= overflow; - if (x >= colRect.x && x < colRect.x + colRect.width) + if (x >= cellRect.x && x < cellRect.x + cellRect.width) { + // We know the column hit now. *aColID = nsXPIDLString::Copy(currCol->GetID()); - currX += colRect.width; + if (currCol->IsCycler()) { + // Cyclers contain only images. Fill this in immediately and return. + nsAutoString image; image.AssignWithConversion("image"); + *aChildElt = nsXPIDLString::Copy(image.GetUnicode()); + } + else + GetItemWithinCellAt(x, cellRect, *aRow, currCol, aChildElt); + break; + } + + currX += cellRect.width; } + return NS_OK; } +nsresult +nsOutlinerBodyFrame::GetItemWithinCellAt(PRInt32 aX, const nsRect& aCellRect, + PRInt32 aRowIndex, + nsOutlinerColumn* aColumn, PRUnichar** aChildElt) +{ + // Obtain the properties for our cell. + PrefillPropertyArray(aRowIndex, aColumn); + mView->GetCellProperties(aRowIndex, aColumn->GetID(), mScratchArray); + + // Resolve style for the cell. + nsCOMPtr cellContext; + GetPseudoStyleContext(nsXULAtoms::mozoutlinercell, getter_AddRefs(cellContext)); + + // Obtain the margins for the cell and then deflate our rect by that + // amount. The cell is assumed to be contained within the deflated rect. + nsRect cellRect(aCellRect); + const nsStyleMargin* cellMarginData = (const nsStyleMargin*)cellContext->GetStyleData(eStyleStruct_Margin); + nsMargin cellMargin; + cellMarginData->GetMargin(cellMargin); + cellRect.Deflate(cellMargin); + + // Adjust the rect for its border and padding. + AdjustForBorderPadding(cellContext, cellRect); + + if (aX < cellRect.x || aX >= cellRect.x + cellRect.width) { + // The user clicked within the cell's margins/borders/padding. This constitutes a click on the cell. + nsAutoString cell; cell.AssignWithConversion("cell"); + *aChildElt = nsXPIDLString::Copy(cell.GetUnicode()); + return NS_OK; + } + + nscoord currX = cellRect.x; + nscoord remainingWidth = cellRect.width; + + // XXX Handle right alignment hit testing. + + if (aColumn->IsPrimary()) { + // If we're the primary column, we have indentation and a twisty. + PRInt32 level; + mView->GetLevel(aRowIndex, &level); + + currX += mIndentation*level; + remainingWidth -= mIndentation*level; + + if (aX < currX) { + // The user clicked within the indentation. + nsAutoString cell; cell.AssignWithConversion("cell"); + *aChildElt = nsXPIDLString::Copy(cell.GetUnicode()); + return NS_OK; + } + + // Always leave space for the twisty. + nsRect twistyRect(currX, cellRect.y, remainingWidth, cellRect.height); + PRBool hasTwisty = PR_FALSE; + PRBool isContainer = PR_FALSE; + mView->IsContainer(aRowIndex, &isContainer); + if (isContainer) { + PRBool isContainerEmpty = PR_FALSE; + mView->IsContainerEmpty(aRowIndex, &isContainerEmpty); + if (!isContainerEmpty) + hasTwisty = PR_TRUE; + } + + // Resolve style for the twisty. + nsCOMPtr twistyContext; + GetPseudoStyleContext(nsXULAtoms::mozoutlinertwisty, getter_AddRefs(twistyContext)); + + // We will treat a click as hitting the twisty if it happens on the margins, borders, padding, + // or content of the twisty object. By allowing a "slop" into the margin, we make it a little + // bit easier for a user to hit the twisty. (We don't want to be too picky here.) + nsRect imageSize = GetImageSize(twistyContext); + const nsStyleMargin* twistyMarginData = (const nsStyleMargin*)twistyContext->GetStyleData(eStyleStruct_Margin); + nsMargin twistyMargin; + twistyMarginData->GetMargin(twistyMargin); + imageSize.Inflate(twistyMargin); + twistyRect.width = imageSize.width; + + // Now we test to see if aX is actually within the twistyRect. If it is, and if the item should + // have a twisty, then we return "twisty". If it is within the rect but we shouldn't have a twisty, + // then we return "cell". + if (aX >= twistyRect.x && aX < twistyRect.x + twistyRect.width) { + if (hasTwisty) { + nsAutoString twisty; twisty.AssignWithConversion("twisty"); + *aChildElt = nsXPIDLString::Copy(twisty.GetUnicode()); + } + else { + nsAutoString cell; cell.AssignWithConversion("cell"); + *aChildElt = nsXPIDLString::Copy(cell.GetUnicode()); + } + return NS_OK; + } + + currX += twistyRect.width; + remainingWidth -= twistyRect.width; + } + + // Now test to see if the user hit the icon for the cell. + nsRect iconRect(currX, cellRect.y, remainingWidth, cellRect.height); + + // Resolve style for the image. + nsCOMPtr imageContext; + GetPseudoStyleContext(nsXULAtoms::mozoutlinerimage, getter_AddRefs(imageContext)); + + nsRect iconSize = GetImageSize(imageContext); + const nsStyleMargin* imageMarginData = (const nsStyleMargin*)imageContext->GetStyleData(eStyleStruct_Margin); + nsMargin imageMargin; + imageMarginData->GetMargin(imageMargin); + iconSize.Inflate(imageMargin); + iconRect.width = iconSize.width; + + if (aX >= iconRect.x && aX < iconRect.x + iconRect.width) { + // The user clicked on the image. + nsAutoString image; image.AssignWithConversion("image"); + *aChildElt = nsXPIDLString::Copy(image.GetUnicode()); + return NS_OK; + } + + // Just assume "text". + // XXX For marquee selection, we'll have to make this more precise and do text measurement. + nsAutoString text; text.AssignWithConversion("text"); + *aChildElt = nsXPIDLString::Copy(text.GetUnicode()); + return NS_OK; +} + + NS_IMETHODIMP nsOutlinerBodyFrame::RowCountChanged(PRInt32 aIndex, PRInt32 aCount) { if (aCount == 0) @@ -854,7 +1002,7 @@ PRInt32 nsOutlinerBodyFrame::GetRowHeight() // + the specified margins. nsCOMPtr rowContext; mScratchArray->Clear(); - GetPseudoStyleContext(mPresContext, nsXULAtoms::mozoutlinerrow, getter_AddRefs(rowContext)); + GetPseudoStyleContext(nsXULAtoms::mozoutlinerrow, getter_AddRefs(rowContext)); if (rowContext) { const nsStylePosition* myPosition = (const nsStylePosition*) rowContext->GetStyleData(eStyleStruct_Position); @@ -881,7 +1029,7 @@ PRInt32 nsOutlinerBodyFrame::GetIndentation() // Look up the correct indentation. It is equal to the specified indentation width. nsCOMPtr indentContext; mScratchArray->Clear(); - GetPseudoStyleContext(mPresContext, nsXULAtoms::mozoutlinerindentation, getter_AddRefs(indentContext)); + GetPseudoStyleContext(nsXULAtoms::mozoutlinerindentation, getter_AddRefs(indentContext)); if (indentContext) { const nsStylePosition* myPosition = (const nsStylePosition*) indentContext->GetStyleData(eStyleStruct_Position); @@ -904,16 +1052,6 @@ nsRect nsOutlinerBodyFrame::GetInnerBox() return r; } -static void -AdjustForBorderPadding(nsIStyleContext* aContext, nsRect& aRect) -{ - nsMargin m(0,0,0,0); - nsStyleBorderPadding bPad; - aContext->GetStyle(eStyleStruct_BorderPaddingShortcut, (nsStyleStruct&)bPad); - bPad.GetBorderPadding(m); - aRect.Deflate(m); -} - // Painting routines NS_IMETHODIMP nsOutlinerBodyFrame::Paint(nsIPresContext* aPresContext, nsIRenderingContext& aRenderingContext, @@ -1019,7 +1157,7 @@ NS_IMETHODIMP nsOutlinerBodyFrame::PaintColumn(nsOutlinerColumn* aColumn, // Resolve style for the column. It contains all the info we need to lay ourselves // out and to paint. nsCOMPtr colContext; - GetPseudoStyleContext(aPresContext, nsXULAtoms::mozoutlinercolumn, getter_AddRefs(colContext)); + GetPseudoStyleContext(nsXULAtoms::mozoutlinercolumn, getter_AddRefs(colContext)); // Obtain the margins for the cell and then deflate our rect by that // amount. The cell is assumed to be contained within the deflated rect. @@ -1055,7 +1193,7 @@ NS_IMETHODIMP nsOutlinerBodyFrame::PaintRow(int aRowIndex, const nsRect& aRowRec // Resolve style for the row. It contains all the info we need to lay ourselves // out and to paint. nsCOMPtr rowContext; - GetPseudoStyleContext(aPresContext, nsXULAtoms::mozoutlinerrow, getter_AddRefs(rowContext)); + GetPseudoStyleContext(nsXULAtoms::mozoutlinerrow, getter_AddRefs(rowContext)); // Obtain the margins for the row and then deflate our rect by that // amount. The row is assumed to be contained within the deflated rect. @@ -1110,7 +1248,7 @@ NS_IMETHODIMP nsOutlinerBodyFrame::PaintCell(int aRowIndex, // Resolve style for the cell. It contains all the info we need to lay ourselves // out and to paint. nsCOMPtr cellContext; - GetPseudoStyleContext(aPresContext, nsXULAtoms::mozoutlinercell, getter_AddRefs(cellContext)); + GetPseudoStyleContext(nsXULAtoms::mozoutlinercell, getter_AddRefs(cellContext)); // Obtain the margins for the cell and then deflate our rect by that // amount. The cell is assumed to be contained within the deflated rect. @@ -1148,7 +1286,7 @@ NS_IMETHODIMP nsOutlinerBodyFrame::PaintCell(int aRowIndex, // Resolve the style to use for the connecting lines. nsCOMPtr lineContext; - GetPseudoStyleContext(aPresContext, nsXULAtoms::mozoutlinerline, getter_AddRefs(lineContext)); + GetPseudoStyleContext(nsXULAtoms::mozoutlinerline, getter_AddRefs(lineContext)); const nsStyleDisplay* displayStyle = (const nsStyleDisplay*)lineContext->GetStyleData(eStyleStruct_Display); if (displayStyle->IsVisibleOrCollapsed() && level && NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) { @@ -1244,7 +1382,7 @@ nsOutlinerBodyFrame::PaintTwisty(int aRowIndex, // Resolve style for the twisty. nsCOMPtr twistyContext; - GetPseudoStyleContext(aPresContext, nsXULAtoms::mozoutlinertwisty, getter_AddRefs(twistyContext)); + GetPseudoStyleContext(nsXULAtoms::mozoutlinertwisty, getter_AddRefs(twistyContext)); // Obtain the margins for the twisty and then deflate our rect by that // amount. The twisty is assumed to be contained within the deflated rect. @@ -1316,7 +1454,7 @@ nsOutlinerBodyFrame::PaintImage(int aRowIndex, { // Resolve style for the image. nsCOMPtr imageContext; - GetPseudoStyleContext(aPresContext, nsXULAtoms::mozoutlinerimage, getter_AddRefs(imageContext)); + GetPseudoStyleContext(nsXULAtoms::mozoutlinerimage, getter_AddRefs(imageContext)); // Obtain the margins for the twisty and then deflate our rect by that // amount. The twisty is assumed to be contained within the deflated rect. @@ -1401,7 +1539,7 @@ NS_IMETHODIMP nsOutlinerBodyFrame::PaintText(int aRowIndex, // Resolve style for the text. It contains all the info we need to lay ourselves // out and to paint. nsCOMPtr textContext; - GetPseudoStyleContext(aPresContext, nsXULAtoms::mozoutlinercelltext, getter_AddRefs(textContext)); + GetPseudoStyleContext(nsXULAtoms::mozoutlinercelltext, getter_AddRefs(textContext)); // Obtain the margins for the text and then deflate our rect by that // amount. The text is assumed to be contained within the deflated rect. @@ -1697,10 +1835,10 @@ nsOutlinerBodyFrame::PositionChanged(PRInt32 aOldIndex, PRInt32& aNewIndex) // The style cache. nsresult -nsOutlinerBodyFrame::GetPseudoStyleContext(nsIPresContext* aPresContext, nsIAtom* aPseudoElement, +nsOutlinerBodyFrame::GetPseudoStyleContext(nsIAtom* aPseudoElement, nsIStyleContext** aResult) { - return mStyleCache.GetStyleContext(this, aPresContext, mContent, mStyleContext, aPseudoElement, + return mStyleCache.GetStyleContext(this, mPresContext, mContent, mStyleContext, aPseudoElement, mScratchArray, aResult); } diff --git a/layout/xul/base/src/outliner/src/nsOutlinerBodyFrame.h b/layout/xul/base/src/outliner/src/nsOutlinerBodyFrame.h index 6273cafb6acd..d8d1912cb57e 100644 --- a/layout/xul/base/src/outliner/src/nsOutlinerBodyFrame.h +++ b/layout/xul/base/src/outliner/src/nsOutlinerBodyFrame.h @@ -303,6 +303,10 @@ protected: // Caches our box object. void SetBoxObject(nsIOutlinerBoxObject* aBoxObject) { mOutlinerBoxObject = aBoxObject; }; + // A helper used when hit testing. + nsresult GetItemWithinCellAt(PRInt32 aX, const nsRect& aCellRect, PRInt32 aRowIndex, + nsOutlinerColumn* aColumn, PRUnichar** aChildElt); + #ifdef USE_IMG2 // Fetch an image from the image cache. nsresult GetImage(PRInt32 aRowIndex, const PRUnichar* aColID, @@ -324,7 +328,7 @@ protected: // Looks up a style context in the style cache. On a cache miss we resolve // the pseudo-styles passed in and place them into the cache. - nsresult GetPseudoStyleContext(nsIPresContext* aPresContext, nsIAtom* aPseudoElement, nsIStyleContext** aResult); + nsresult GetPseudoStyleContext(nsIAtom* aPseudoElement, nsIStyleContext** aResult); // Builds our cache of column info. void EnsureColumns(); diff --git a/layout/xul/base/src/outliner/src/nsOutlinerBoxObject.cpp b/layout/xul/base/src/outliner/src/nsOutlinerBoxObject.cpp index 445e39c51778..bb6ce208a37a 100644 --- a/layout/xul/base/src/outliner/src/nsOutlinerBoxObject.cpp +++ b/layout/xul/base/src/outliner/src/nsOutlinerBoxObject.cpp @@ -280,11 +280,12 @@ NS_IMETHODIMP nsOutlinerBoxObject::InvalidateScrollbar() return NS_OK; } -NS_IMETHODIMP nsOutlinerBoxObject::GetCellAt(PRInt32 x, PRInt32 y, PRInt32 *row, PRUnichar **colID) +NS_IMETHODIMP nsOutlinerBoxObject::GetCellAt(PRInt32 x, PRInt32 y, PRInt32 *row, PRUnichar **colID, + PRUnichar** childElt) { nsIOutlinerBoxObject* body = GetOutlinerBody(); if (body) - return body->GetCellAt(x, y, row, colID); + return body->GetCellAt(x, y, row, colID, childElt); return NS_OK; } diff --git a/layout/xul/base/src/tree/public/nsITreeBoxObject.idl b/layout/xul/base/src/tree/public/nsITreeBoxObject.idl index cf255c040987..bde4a5302602 100644 --- a/layout/xul/base/src/tree/public/nsITreeBoxObject.idl +++ b/layout/xul/base/src/tree/public/nsITreeBoxObject.idl @@ -78,8 +78,10 @@ interface nsIOutlinerBoxObject : nsISupports void invalidateRange(in long startIndex, in long endIndex); void invalidateScrollbar(); - // A hit test that can tell you what cell the mouse is over. - void getCellAt(in long x, in long y, out long row, out wstring colID); + // A hit test that can tell you what cell the mouse is over. Row is the row index + // hit. ColID is the column hit. ChildElt is the pseudoelement hit: this can have + // values of "cell", "twisty", "image", and "text". + void getCellAt(in long x, in long y, out long row, out wstring colID, out wstring childElt); // The view is responsible for calling these notification methods when // rows are added or removed. Index is the position at which the new diff --git a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp index dc2897bed1e8..669cd16e8687 100644 --- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp +++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp @@ -329,6 +329,16 @@ NS_IMETHODIMP nsOutlinerBodyFrame::Reflow(nsIPresContext* aPresContext, return NS_OK; } +static void +AdjustForBorderPadding(nsIStyleContext* aContext, nsRect& aRect) +{ + nsMargin m(0,0,0,0); + nsStyleBorderPadding bPad; + aContext->GetStyle(eStyleStruct_BorderPaddingShortcut, (nsStyleStruct&)bPad); + bPad.GetBorderPadding(m); + aRect.Deflate(m); +} + NS_IMETHODIMP nsOutlinerBodyFrame::GetView(nsIOutlinerView * *aView) { *aView = mView; @@ -547,7 +557,8 @@ NS_IMETHODIMP nsOutlinerBodyFrame::InvalidateScrollbar() return NS_OK; } -NS_IMETHODIMP nsOutlinerBodyFrame::GetCellAt(PRInt32 aX, PRInt32 aY, PRInt32* aRow, PRUnichar** aColID) +NS_IMETHODIMP nsOutlinerBodyFrame::GetCellAt(PRInt32 aX, PRInt32 aY, PRInt32* aRow, PRUnichar** aColID, + PRUnichar** aChildElt) { // Ensure we have a row height. if (mRowHeight == 0) @@ -591,19 +602,156 @@ NS_IMETHODIMP nsOutlinerBodyFrame::GetCellAt(PRInt32 aX, PRInt32 aY, PRInt32* aR nscoord currX = mInnerBox.x; for (nsOutlinerColumn* currCol = mColumns; currCol && currX < mInnerBox.x+mInnerBox.width; currCol = currCol->GetNext()) { - nsRect colRect(currX, mInnerBox.y, currCol->GetWidth(), mInnerBox.height); - PRInt32 overflow = colRect.x+colRect.width-(mInnerBox.x+mInnerBox.width); + nsRect cellRect(currX, mInnerBox.y+mRowHeight*(*aRow-mTopRowIndex), currCol->GetWidth(), mRowHeight); + PRInt32 overflow = cellRect.x+cellRect.width-(mInnerBox.x+mInnerBox.width); if (overflow > 0) - colRect.width -= overflow; + cellRect.width -= overflow; - if (x >= colRect.x && x < colRect.x + colRect.width) + if (x >= cellRect.x && x < cellRect.x + cellRect.width) { + // We know the column hit now. *aColID = nsXPIDLString::Copy(currCol->GetID()); - currX += colRect.width; + if (currCol->IsCycler()) { + // Cyclers contain only images. Fill this in immediately and return. + nsAutoString image; image.AssignWithConversion("image"); + *aChildElt = nsXPIDLString::Copy(image.GetUnicode()); + } + else + GetItemWithinCellAt(x, cellRect, *aRow, currCol, aChildElt); + break; + } + + currX += cellRect.width; } + return NS_OK; } +nsresult +nsOutlinerBodyFrame::GetItemWithinCellAt(PRInt32 aX, const nsRect& aCellRect, + PRInt32 aRowIndex, + nsOutlinerColumn* aColumn, PRUnichar** aChildElt) +{ + // Obtain the properties for our cell. + PrefillPropertyArray(aRowIndex, aColumn); + mView->GetCellProperties(aRowIndex, aColumn->GetID(), mScratchArray); + + // Resolve style for the cell. + nsCOMPtr cellContext; + GetPseudoStyleContext(nsXULAtoms::mozoutlinercell, getter_AddRefs(cellContext)); + + // Obtain the margins for the cell and then deflate our rect by that + // amount. The cell is assumed to be contained within the deflated rect. + nsRect cellRect(aCellRect); + const nsStyleMargin* cellMarginData = (const nsStyleMargin*)cellContext->GetStyleData(eStyleStruct_Margin); + nsMargin cellMargin; + cellMarginData->GetMargin(cellMargin); + cellRect.Deflate(cellMargin); + + // Adjust the rect for its border and padding. + AdjustForBorderPadding(cellContext, cellRect); + + if (aX < cellRect.x || aX >= cellRect.x + cellRect.width) { + // The user clicked within the cell's margins/borders/padding. This constitutes a click on the cell. + nsAutoString cell; cell.AssignWithConversion("cell"); + *aChildElt = nsXPIDLString::Copy(cell.GetUnicode()); + return NS_OK; + } + + nscoord currX = cellRect.x; + nscoord remainingWidth = cellRect.width; + + // XXX Handle right alignment hit testing. + + if (aColumn->IsPrimary()) { + // If we're the primary column, we have indentation and a twisty. + PRInt32 level; + mView->GetLevel(aRowIndex, &level); + + currX += mIndentation*level; + remainingWidth -= mIndentation*level; + + if (aX < currX) { + // The user clicked within the indentation. + nsAutoString cell; cell.AssignWithConversion("cell"); + *aChildElt = nsXPIDLString::Copy(cell.GetUnicode()); + return NS_OK; + } + + // Always leave space for the twisty. + nsRect twistyRect(currX, cellRect.y, remainingWidth, cellRect.height); + PRBool hasTwisty = PR_FALSE; + PRBool isContainer = PR_FALSE; + mView->IsContainer(aRowIndex, &isContainer); + if (isContainer) { + PRBool isContainerEmpty = PR_FALSE; + mView->IsContainerEmpty(aRowIndex, &isContainerEmpty); + if (!isContainerEmpty) + hasTwisty = PR_TRUE; + } + + // Resolve style for the twisty. + nsCOMPtr twistyContext; + GetPseudoStyleContext(nsXULAtoms::mozoutlinertwisty, getter_AddRefs(twistyContext)); + + // We will treat a click as hitting the twisty if it happens on the margins, borders, padding, + // or content of the twisty object. By allowing a "slop" into the margin, we make it a little + // bit easier for a user to hit the twisty. (We don't want to be too picky here.) + nsRect imageSize = GetImageSize(twistyContext); + const nsStyleMargin* twistyMarginData = (const nsStyleMargin*)twistyContext->GetStyleData(eStyleStruct_Margin); + nsMargin twistyMargin; + twistyMarginData->GetMargin(twistyMargin); + imageSize.Inflate(twistyMargin); + twistyRect.width = imageSize.width; + + // Now we test to see if aX is actually within the twistyRect. If it is, and if the item should + // have a twisty, then we return "twisty". If it is within the rect but we shouldn't have a twisty, + // then we return "cell". + if (aX >= twistyRect.x && aX < twistyRect.x + twistyRect.width) { + if (hasTwisty) { + nsAutoString twisty; twisty.AssignWithConversion("twisty"); + *aChildElt = nsXPIDLString::Copy(twisty.GetUnicode()); + } + else { + nsAutoString cell; cell.AssignWithConversion("cell"); + *aChildElt = nsXPIDLString::Copy(cell.GetUnicode()); + } + return NS_OK; + } + + currX += twistyRect.width; + remainingWidth -= twistyRect.width; + } + + // Now test to see if the user hit the icon for the cell. + nsRect iconRect(currX, cellRect.y, remainingWidth, cellRect.height); + + // Resolve style for the image. + nsCOMPtr imageContext; + GetPseudoStyleContext(nsXULAtoms::mozoutlinerimage, getter_AddRefs(imageContext)); + + nsRect iconSize = GetImageSize(imageContext); + const nsStyleMargin* imageMarginData = (const nsStyleMargin*)imageContext->GetStyleData(eStyleStruct_Margin); + nsMargin imageMargin; + imageMarginData->GetMargin(imageMargin); + iconSize.Inflate(imageMargin); + iconRect.width = iconSize.width; + + if (aX >= iconRect.x && aX < iconRect.x + iconRect.width) { + // The user clicked on the image. + nsAutoString image; image.AssignWithConversion("image"); + *aChildElt = nsXPIDLString::Copy(image.GetUnicode()); + return NS_OK; + } + + // Just assume "text". + // XXX For marquee selection, we'll have to make this more precise and do text measurement. + nsAutoString text; text.AssignWithConversion("text"); + *aChildElt = nsXPIDLString::Copy(text.GetUnicode()); + return NS_OK; +} + + NS_IMETHODIMP nsOutlinerBodyFrame::RowCountChanged(PRInt32 aIndex, PRInt32 aCount) { if (aCount == 0) @@ -854,7 +1002,7 @@ PRInt32 nsOutlinerBodyFrame::GetRowHeight() // + the specified margins. nsCOMPtr rowContext; mScratchArray->Clear(); - GetPseudoStyleContext(mPresContext, nsXULAtoms::mozoutlinerrow, getter_AddRefs(rowContext)); + GetPseudoStyleContext(nsXULAtoms::mozoutlinerrow, getter_AddRefs(rowContext)); if (rowContext) { const nsStylePosition* myPosition = (const nsStylePosition*) rowContext->GetStyleData(eStyleStruct_Position); @@ -881,7 +1029,7 @@ PRInt32 nsOutlinerBodyFrame::GetIndentation() // Look up the correct indentation. It is equal to the specified indentation width. nsCOMPtr indentContext; mScratchArray->Clear(); - GetPseudoStyleContext(mPresContext, nsXULAtoms::mozoutlinerindentation, getter_AddRefs(indentContext)); + GetPseudoStyleContext(nsXULAtoms::mozoutlinerindentation, getter_AddRefs(indentContext)); if (indentContext) { const nsStylePosition* myPosition = (const nsStylePosition*) indentContext->GetStyleData(eStyleStruct_Position); @@ -904,16 +1052,6 @@ nsRect nsOutlinerBodyFrame::GetInnerBox() return r; } -static void -AdjustForBorderPadding(nsIStyleContext* aContext, nsRect& aRect) -{ - nsMargin m(0,0,0,0); - nsStyleBorderPadding bPad; - aContext->GetStyle(eStyleStruct_BorderPaddingShortcut, (nsStyleStruct&)bPad); - bPad.GetBorderPadding(m); - aRect.Deflate(m); -} - // Painting routines NS_IMETHODIMP nsOutlinerBodyFrame::Paint(nsIPresContext* aPresContext, nsIRenderingContext& aRenderingContext, @@ -1019,7 +1157,7 @@ NS_IMETHODIMP nsOutlinerBodyFrame::PaintColumn(nsOutlinerColumn* aColumn, // Resolve style for the column. It contains all the info we need to lay ourselves // out and to paint. nsCOMPtr colContext; - GetPseudoStyleContext(aPresContext, nsXULAtoms::mozoutlinercolumn, getter_AddRefs(colContext)); + GetPseudoStyleContext(nsXULAtoms::mozoutlinercolumn, getter_AddRefs(colContext)); // Obtain the margins for the cell and then deflate our rect by that // amount. The cell is assumed to be contained within the deflated rect. @@ -1055,7 +1193,7 @@ NS_IMETHODIMP nsOutlinerBodyFrame::PaintRow(int aRowIndex, const nsRect& aRowRec // Resolve style for the row. It contains all the info we need to lay ourselves // out and to paint. nsCOMPtr rowContext; - GetPseudoStyleContext(aPresContext, nsXULAtoms::mozoutlinerrow, getter_AddRefs(rowContext)); + GetPseudoStyleContext(nsXULAtoms::mozoutlinerrow, getter_AddRefs(rowContext)); // Obtain the margins for the row and then deflate our rect by that // amount. The row is assumed to be contained within the deflated rect. @@ -1110,7 +1248,7 @@ NS_IMETHODIMP nsOutlinerBodyFrame::PaintCell(int aRowIndex, // Resolve style for the cell. It contains all the info we need to lay ourselves // out and to paint. nsCOMPtr cellContext; - GetPseudoStyleContext(aPresContext, nsXULAtoms::mozoutlinercell, getter_AddRefs(cellContext)); + GetPseudoStyleContext(nsXULAtoms::mozoutlinercell, getter_AddRefs(cellContext)); // Obtain the margins for the cell and then deflate our rect by that // amount. The cell is assumed to be contained within the deflated rect. @@ -1148,7 +1286,7 @@ NS_IMETHODIMP nsOutlinerBodyFrame::PaintCell(int aRowIndex, // Resolve the style to use for the connecting lines. nsCOMPtr lineContext; - GetPseudoStyleContext(aPresContext, nsXULAtoms::mozoutlinerline, getter_AddRefs(lineContext)); + GetPseudoStyleContext(nsXULAtoms::mozoutlinerline, getter_AddRefs(lineContext)); const nsStyleDisplay* displayStyle = (const nsStyleDisplay*)lineContext->GetStyleData(eStyleStruct_Display); if (displayStyle->IsVisibleOrCollapsed() && level && NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) { @@ -1244,7 +1382,7 @@ nsOutlinerBodyFrame::PaintTwisty(int aRowIndex, // Resolve style for the twisty. nsCOMPtr twistyContext; - GetPseudoStyleContext(aPresContext, nsXULAtoms::mozoutlinertwisty, getter_AddRefs(twistyContext)); + GetPseudoStyleContext(nsXULAtoms::mozoutlinertwisty, getter_AddRefs(twistyContext)); // Obtain the margins for the twisty and then deflate our rect by that // amount. The twisty is assumed to be contained within the deflated rect. @@ -1316,7 +1454,7 @@ nsOutlinerBodyFrame::PaintImage(int aRowIndex, { // Resolve style for the image. nsCOMPtr imageContext; - GetPseudoStyleContext(aPresContext, nsXULAtoms::mozoutlinerimage, getter_AddRefs(imageContext)); + GetPseudoStyleContext(nsXULAtoms::mozoutlinerimage, getter_AddRefs(imageContext)); // Obtain the margins for the twisty and then deflate our rect by that // amount. The twisty is assumed to be contained within the deflated rect. @@ -1401,7 +1539,7 @@ NS_IMETHODIMP nsOutlinerBodyFrame::PaintText(int aRowIndex, // Resolve style for the text. It contains all the info we need to lay ourselves // out and to paint. nsCOMPtr textContext; - GetPseudoStyleContext(aPresContext, nsXULAtoms::mozoutlinercelltext, getter_AddRefs(textContext)); + GetPseudoStyleContext(nsXULAtoms::mozoutlinercelltext, getter_AddRefs(textContext)); // Obtain the margins for the text and then deflate our rect by that // amount. The text is assumed to be contained within the deflated rect. @@ -1697,10 +1835,10 @@ nsOutlinerBodyFrame::PositionChanged(PRInt32 aOldIndex, PRInt32& aNewIndex) // The style cache. nsresult -nsOutlinerBodyFrame::GetPseudoStyleContext(nsIPresContext* aPresContext, nsIAtom* aPseudoElement, +nsOutlinerBodyFrame::GetPseudoStyleContext(nsIAtom* aPseudoElement, nsIStyleContext** aResult) { - return mStyleCache.GetStyleContext(this, aPresContext, mContent, mStyleContext, aPseudoElement, + return mStyleCache.GetStyleContext(this, mPresContext, mContent, mStyleContext, aPseudoElement, mScratchArray, aResult); } diff --git a/layout/xul/base/src/tree/src/nsTreeBodyFrame.h b/layout/xul/base/src/tree/src/nsTreeBodyFrame.h index 6273cafb6acd..d8d1912cb57e 100644 --- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.h +++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.h @@ -303,6 +303,10 @@ protected: // Caches our box object. void SetBoxObject(nsIOutlinerBoxObject* aBoxObject) { mOutlinerBoxObject = aBoxObject; }; + // A helper used when hit testing. + nsresult GetItemWithinCellAt(PRInt32 aX, const nsRect& aCellRect, PRInt32 aRowIndex, + nsOutlinerColumn* aColumn, PRUnichar** aChildElt); + #ifdef USE_IMG2 // Fetch an image from the image cache. nsresult GetImage(PRInt32 aRowIndex, const PRUnichar* aColID, @@ -324,7 +328,7 @@ protected: // Looks up a style context in the style cache. On a cache miss we resolve // the pseudo-styles passed in and place them into the cache. - nsresult GetPseudoStyleContext(nsIPresContext* aPresContext, nsIAtom* aPseudoElement, nsIStyleContext** aResult); + nsresult GetPseudoStyleContext(nsIAtom* aPseudoElement, nsIStyleContext** aResult); // Builds our cache of column info. void EnsureColumns(); diff --git a/layout/xul/base/src/tree/src/nsTreeBoxObject.cpp b/layout/xul/base/src/tree/src/nsTreeBoxObject.cpp index 445e39c51778..bb6ce208a37a 100644 --- a/layout/xul/base/src/tree/src/nsTreeBoxObject.cpp +++ b/layout/xul/base/src/tree/src/nsTreeBoxObject.cpp @@ -280,11 +280,12 @@ NS_IMETHODIMP nsOutlinerBoxObject::InvalidateScrollbar() return NS_OK; } -NS_IMETHODIMP nsOutlinerBoxObject::GetCellAt(PRInt32 x, PRInt32 y, PRInt32 *row, PRUnichar **colID) +NS_IMETHODIMP nsOutlinerBoxObject::GetCellAt(PRInt32 x, PRInt32 y, PRInt32 *row, PRUnichar **colID, + PRUnichar** childElt) { nsIOutlinerBoxObject* body = GetOutlinerBody(); if (body) - return body->GetCellAt(x, y, row, colID); + return body->GetCellAt(x, y, row, colID, childElt); return NS_OK; } diff --git a/xpfe/global/resources/content/outlinerBindings.xml b/xpfe/global/resources/content/outlinerBindings.xml index 1c4645fa1687..34b6c9038469 100644 --- a/xpfe/global/resources/content/outlinerBindings.xml +++ b/xpfe/global/resources/content/outlinerBindings.xml @@ -409,17 +409,20 @@ if (!event.ctrlKey && !event.shiftKey && !event.metaKey) { var row = {}; var col = {}; + var obj = {}; var b = this.parentNode.outlinerBoxObject; - b.getCellAt(event.clientX, event.clientY, row, col); + b.getCellAt(event.clientX, event.clientY, row, col, obj); - var column = document.getElementById(col.value); - var cycler = column.getAttribute('cycler') == 'true'; + if (obj.value != "twisty") { + var column = document.getElementById(col.value); + var cycler = column.getAttribute('cycler') == 'true'; - if (cycler) - b.view.cycleCell(row.value, col.value); - else - if (!b.selection.isSelected(row.value)) - b.selection.select(row.value); + if (cycler) + b.view.cycleCell(row.value, col.value); + else + if (!b.selection.isSelected(row.value)) + b.selection.select(row.value); + } } ]]> @@ -431,8 +434,13 @@ if (event.button != 0) return; var row = {}; var col = {}; + var obj = {}; var b = this.parentNode.outlinerBoxObject; - b.getCellAt(event.clientX, event.clientY, row, col); + b.getCellAt(event.clientX, event.clientY, row, col, obj); + if (obj.value == "twisty") { + b.view.toggleOpenState(row.value); + return; + } var augment = event.ctrlKey || event.metaKey; if (event.shiftKey) { b.selection.rangedSelect(-1, row.value, augment); @@ -462,10 +470,13 @@