fix for 47105, retooling of tree d&d to not be so confusing, to use the style system a little more, and to handle the case where a tree won't let you drop anything between rows.

This commit is contained in:
pinkerton%netscape.com 2000-08-15 18:23:34 +00:00
parent 4d6f0e2dd4
commit 1a3893097f
7 changed files with 148 additions and 108 deletions

View File

@ -155,8 +155,10 @@ XUL_ATOM(ddDropOn, "dd-dropon")
XUL_ATOM(ddTriggerRepaintSorted, "dd-triggerrepaintsorted")
XUL_ATOM(ddTriggerRepaintRestore, "dd-triggerrepaintrestore")
XUL_ATOM(ddTriggerRepaint, "dd-triggerrepaint")
XUL_ATOM(ddNoDropBetweenRows, "dd-nodropbetweenrows")
XUL_ATOM(container, "container")
XUL_ATOM(ddDragDropArea, "dragdroparea")
XUL_ATOM(ddDropMarker, ":-moz-drop-marker")
XUL_ATOM(widget, "widget")
XUL_ATOM(window, "window")

View File

@ -37,6 +37,9 @@
#include "nsIDOMEventTarget.h"
#include "nsIView.h"
#include "nsIFrame.h"
#include "nsXULTreeGroupFrame.h"
#include "nsXULTreeOuterGroupFrame.h"
NS_IMPL_ADDREF(nsTreeItemDragCapturer)
NS_IMPL_RELEASE(nsTreeItemDragCapturer)
@ -49,7 +52,7 @@ NS_IMPL_QUERY_INTERFACE2(nsTreeItemDragCapturer, nsIDOMEventListener, nsIDOMDrag
// Init member variables. We can't really do much of anything important here because
// any subframes might not be totally intialized yet, or in the hash table
//
nsTreeItemDragCapturer :: nsTreeItemDragCapturer ( nsIFrame* inTreeItem, nsIPresContext* inPresContext )
nsTreeItemDragCapturer :: nsTreeItemDragCapturer ( nsXULTreeGroupFrame* inTreeItem, nsIPresContext* inPresContext )
: mTreeItem(inTreeItem), mPresContext(inPresContext), mCurrentDropLoc(kNoDropLoc)
{
NS_INIT_REFCNT();
@ -110,18 +113,92 @@ void
nsTreeItemDragCapturer :: ComputeDropPosition ( nsIDOMEvent* aDragEvent, nscoord* outYLoc,
PRBool* outBefore, PRBool* outDropOnMe )
{
*outYLoc = 0;
*outYLoc = kNoDropLoc;
*outBefore = PR_FALSE;
*outDropOnMe = PR_FALSE;
//
// Get the mouse coordinates from the DOM event, but they will be in the
// window/widget coordinate system. We must first get them into the frame-relative
// coordinate system. Yuck.
//
float p2t;
mPresContext->GetScaledPixelsToTwips(&p2t);
nscoord onePixel = NSIntPixelsToTwips(1, p2t);
// Gecko trickery to get mouse coordinates into the right coordinate system for
// comparison with the row.
nsPoint pnt(0,0);
nsRect rowRect;
ConvertEventCoordsToRowCoords ( aDragEvent, &pnt, &rowRect);
// check the outer group frame to see if the tree allows drops between rows. If it
// doesn't then basically the entire row is a drop zone for a container.
PRBool allowsDropsBetweenRows = PR_TRUE;
nsXULTreeOuterGroupFrame* outerGroup = mTreeItem->GetOuterFrame();
if ( outerGroup )
allowsDropsBetweenRows = outerGroup->CanDropBetweenRows();
// now check if item is a container
PRBool isContainer = PR_FALSE;
nsCOMPtr<nsIContent> treeItemContent;
mTreeItem->GetContent ( getter_AddRefs(treeItemContent) );
nsCOMPtr<nsIDOMElement> treeItemNode ( do_QueryInterface(treeItemContent) );
if ( treeItemNode ) {
nsAutoString value;
treeItemNode->GetAttribute(NS_ConvertASCIItoUCS2("container"), value); // can't use an atom here =(
isContainer = value.EqualsWithConversion("true");
}
else
NS_WARNING("Not a DOM element");
if ( allowsDropsBetweenRows ) {
// if we have a container, the area is broken up into 3 pieces (top, middle, bottom). If
// it isn't it's only broken up into two (top and bottom)
if ( isContainer ) {
if (pnt.y <= (rowRect.y + (rowRect.height / 4))) {
*outBefore = PR_TRUE;
*outYLoc = 0;
}
else if (pnt.y >= (rowRect.y + PRInt32(float(rowRect.height) *0.75))) {
*outBefore = PR_FALSE;
*outYLoc = rowRect.y + rowRect.height - onePixel;
}
else {
// we're on the container
*outDropOnMe = PR_TRUE;
*outYLoc = kContainerDropLoc;
}
} // if row is a container
else {
if (pnt.y <= (rowRect.y + (rowRect.height / 2))) {
*outBefore = PR_TRUE;
*outYLoc = 0;
}
else
*outYLoc = rowRect.y + rowRect.height - onePixel;
} // else is not a container
} // if can drop between rows
else {
// the tree doesn't allow drops between rows, therefore only drops on containers
// are valid. For this case, make the entire cell a dropzone for this row as there
// is no concept of above and below.
if ( isContainer ) {
*outYLoc = kContainerDropLoc;
*outDropOnMe = PR_TRUE;
}
} // else only allow drops on containers
} // ComputeDropPosition
//
// ConvertEventCoordsToRowCoords
//
// Get the mouse coordinates from the DOM event, but they will be in the
// window/widget coordinate system. We must first get them into the frame-relative
// coordinate system of the row. Yuck.
//
void
nsTreeItemDragCapturer :: ConvertEventCoordsToRowCoords ( nsIDOMEvent* inDragEvent, nsPoint* outCoords, nsRect* outRowRect )
{
// get mouse coordinates and translate them into twips
nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(aDragEvent));
nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(inDragEvent));
PRInt32 x,y = 0;
mouseEvent->GetClientX(&x);
mouseEvent->GetClientY(&y);
@ -133,11 +210,10 @@ nsTreeItemDragCapturer :: ComputeDropPosition ( nsIDOMEvent* aDragEvent, nscoord
// get the rect of the row (not the tree item) that the mouse is over. This is
// where we need to start computing things from.
nsRect rowRect;
nsIFrame* rowFrame;
mTreeItem->FirstChild(mPresContext, nsnull, &rowFrame);
NS_ASSERTION ( rowFrame, "couldn't get rowGroup's row frame" );
rowFrame->GetRect(rowRect);
rowFrame->GetRect(*outRowRect);
// compute the offset to top level in twips
float t2p;
@ -167,47 +243,11 @@ nsTreeItemDragCapturer :: ComputeDropPosition ( nsIDOMEvent* aDragEvent, nscoord
nscoord viewOffsetToParentX = 0, viewOffsetToParentY = 0;
containingView->GetPosition ( &viewOffsetToParentX, &viewOffsetToParentY );
pnt.MoveBy ( -viewOffsetToParentX, -viewOffsetToParentY );
// now check if item is a container
PRBool isContainer = PR_FALSE;
nsCOMPtr<nsIContent> treeItemContent;
mTreeItem->GetContent ( getter_AddRefs(treeItemContent) );
nsCOMPtr<nsIDOMElement> treeItemNode ( do_QueryInterface(treeItemContent) );
if ( treeItemNode ) {
nsAutoString value;
treeItemNode->GetAttribute(NS_ConvertASCIItoUCS2("container"), value); // can't use an atom here =(
isContainer = value.EqualsWithConversion("true");
}
else
NS_WARNING("Not a DOM element");
// if we have a container, the area is broken up into 3 pieces (top, middle, bottom). If
// it isn't it's only broken up into two (top and bottom)
if ( isContainer ) {
if (pnt.y <= (rowRect.y + (rowRect.height / 4))) {
*outBefore = PR_TRUE;
*outYLoc = 0;
}
else if (pnt.y >= (rowRect.y + PRInt32(float(rowRect.height) *0.75))) {
*outBefore = PR_FALSE;
*outYLoc = rowRect.y + rowRect.height - onePixel;
}
else {
// we're on the container
*outDropOnMe = PR_TRUE;
*outYLoc = kContainerDropLoc;
}
} // if row is a container
else {
if (pnt.y <= (rowRect.y + (rowRect.height / 2))) {
*outBefore = PR_TRUE;
*outYLoc = 0;
}
else
*outYLoc = rowRect.y + rowRect.height - onePixel;
} // else is not a container
} // ComputeDropPosition
if ( outCoords )
*outCoords = pnt;
} // ConvertEventCoordsToRowCoords
//
@ -240,7 +280,6 @@ nsTreeItemDragCapturer::DragOver(nsIDOMEvent* aDragEvent)
if ( content ) {
char buffer[10];
// need the cast, because on some platforms, PR[U]int32 != long, but we're using "%ld"
sprintf(buffer, "%d", yLoc);
content->SetAttribute ( kNameSpaceID_None, nsXULAtoms::ddDropLocationCoord, NS_ConvertASCIItoUCS2(buffer), PR_TRUE );
content->SetAttribute ( kNameSpaceID_None, nsXULAtoms::ddDropLocation, NS_ConvertASCIItoUCS2(beforeMe ? "true" : "false"), PR_FALSE );

View File

@ -30,7 +30,9 @@
class nsIPresContext;
class nsIDOMEvent;
class nsIFrame;
class nsXULTreeGroupFrame;
class nsPoint;
class nsRect;
class nsTreeItemDragCapturer : public nsIDOMDragListener
{
@ -43,7 +45,7 @@ public:
} ;
// default ctor and dtor
nsTreeItemDragCapturer ( nsIFrame* inTreeItem, nsIPresContext* inPresContext );
nsTreeItemDragCapturer ( nsXULTreeGroupFrame* inTreeItem, nsIPresContext* inPresContext );
virtual ~nsTreeItemDragCapturer();
// interfaces for addref and release and queryinterface
@ -65,11 +67,16 @@ protected:
// middle region of the row height. If this is the case, |outBefore| is meaningless.
void ComputeDropPosition(nsIDOMEvent* aDragEvent, nscoord* outYLoc, PRBool* outBefore, PRBool* outDropOnMe);
// Take the mouse coordinates from the DOM event and convert them into frame-relative
// coordinates.
void ConvertEventCoordsToRowCoords ( nsIDOMEvent* inDragEvent, nsPoint* outCoords,
nsRect* outRowRect ) ;
// Since there are so many nested tree items, we have to weed out when the event is not
// really for us.
PRBool IsEventTargetMyTreeItem ( nsIDOMEvent* inEvent ) ;
nsIFrame* mTreeItem; // rowGroup owns me, don't be circular
nsXULTreeGroupFrame* mTreeItem; // rowGroup owns me, don't be circular
nsIPresContext* mPresContext; // weak reference
PRInt32 mCurrentDropLoc;

View File

@ -714,8 +714,7 @@ nsXULTreeGroupFrame :: PaintDropFeedback ( nsIPresContext* aPresContext, nsIRend
PRBool aPaintSorted )
{
// lookup the drop marker color. default to black if not found.
nsCOMPtr<nsIAtom> atom ( getter_AddRefs(NS_NewAtom(":-moz-drop-marker")) );
nscolor color = GetColorFromStyleContext ( aPresContext, atom, NS_RGB(0,0,0) ) ;
nscolor color = GetColorFromStyleContext ( aPresContext, nsXULAtoms::ddDropMarker, NS_RGB(0,0,0) );
// find the twips-to-pixels conversion. We have decided not to cache this for
// space reasons.
@ -769,50 +768,25 @@ nsXULTreeGroupFrame :: PaintSortedDropFeedback ( nscolor inColor, nsIRenderingCo
//
// PaintOnContainerDropFeedback
//
// Draws the drop feedback for when the tree is sorted, so line-item drop feedback is
// not appliable.
// Draws the drop feedback for when the row is a container and it is open. We let
// CSS handle the operation of painting the row bg so it's skinnable.
//
void
nsXULTreeGroupFrame :: PaintOnContainerDropFeedback ( nscolor inColor, nsIRenderingContext& inRenderingContext,
nsIPresContext* inPresContext, float & inPixelsToTwips )
{
// lookup the color for the bg of the selected cell. default to gray if not found.
nsCOMPtr<nsIAtom> atom ( getter_AddRefs(NS_NewAtom(":-moz-drop-container-bg")) );
nscolor bgColor = GetColorFromStyleContext ( inPresContext, atom, NS_RGB(0xDD, 0xDD, 0xDD) ) ;
// paint the cell's bg...we really want to muck with the titled buttons, but a) there
// isn't any support for that yet, and b) we don't really know what's going
// to be there in the cell as far as anonymous content goes...
nsRect cellBounds;
nsIFrame* treeRow;
FirstChild ( inPresContext, nsnull, &treeRow );
if ( !treeRow ) return;
nsIFrame* treeCell;
treeRow->FirstChild ( inPresContext, nsnull, &treeCell );
if ( !treeCell ) return;
treeCell->GetRect ( cellBounds );
inRenderingContext.SetColor ( bgColor );
inRenderingContext.FillRect ( cellBounds );
PRInt32 horizIndent = 0;
if ( IsOpenContainer() ) {
PRInt32 horizIndent = 0;
nsIFrame* firstChild = nsnull;
FindFirstChildTreeItemFrame ( inPresContext, &firstChild );
if ( firstChild )
horizIndent = FindIndentation(inPresContext, firstChild);
inRenderingContext.SetColor(inColor);
nsRect dividingLine ( horizIndent, mRect.height - NSToIntRound(2 * inPixelsToTwips),
NSToIntRound(50 * inPixelsToTwips), NSToIntRound(2 * inPixelsToTwips) );
inRenderingContext.DrawRect ( dividingLine );
}
else {
// for the case where the container is closed (it doesn't have any children)
// all we can do is get our own indentation and add the hardcoded indent level
// since we don't really know...The indent level is currently hardcoded in
// the treeIndentation frame to 16..
horizIndent = FindIndentation(inPresContext, this) + NSToIntRound(16 * inPixelsToTwips);
}
inRenderingContext.SetColor(inColor);
nsRect dividingLine ( horizIndent, mRect.height - NSToIntRound(2 * inPixelsToTwips),
NSToIntRound(50 * inPixelsToTwips), NSToIntRound(2 * inPixelsToTwips) );
inRenderingContext.DrawRect ( dividingLine );
} // PaintOnContainerDropFeedback
@ -820,30 +794,33 @@ nsXULTreeGroupFrame :: PaintOnContainerDropFeedback ( nscolor inColor, nsIRender
//
// PaintInBetweenDropFeedback
//
// Draw the feedback for when the drop is to go in between two nodes
// Draw the feedback for when the drop is to go in between two nodes, but only if the tree
// allows that.
//
void
nsXULTreeGroupFrame :: PaintInBetweenDropFeedback ( nscolor inColor, nsIRenderingContext& inRenderingContext,
nsIPresContext* inPresContext, float & inPixelsToTwips )
{
// the normal case is that we can just look at this frame to find the indentation we need. However,
// when we're an _open container_ and are being asked to draw the line _after_, we need to use the
// indentation of our first child instead. ick.
PRInt32 horizIndent = 0;
if ( IsOpenContainer() && mYDropLoc > 0 ) {
nsIFrame* firstChild = nsnull;
FindFirstChildTreeItemFrame ( inPresContext, &firstChild );
if ( firstChild )
horizIndent = FindIndentation(inPresContext, firstChild);
} // if open container and drop after
else
horizIndent = FindIndentation(inPresContext, this);
if ( mOuterFrame->CanDropBetweenRows() ) {
// the normal case is that we can just look at this frame to find the indentation we need. However,
// when we're an _open container_ and are being asked to draw the line _after_, we need to use the
// indentation of our first child instead. ick.
PRInt32 horizIndent = 0;
if ( IsOpenContainer() && mYDropLoc > 0 ) {
nsIFrame* firstChild = nsnull;
FindFirstChildTreeItemFrame ( inPresContext, &firstChild );
if ( firstChild )
horizIndent = FindIndentation(inPresContext, firstChild);
} // if open container and drop after
else
horizIndent = FindIndentation(inPresContext, this);
inRenderingContext.SetColor(inColor);
nsRect dividingLine ( horizIndent, mYDropLoc,
NSToIntRound(50 * inPixelsToTwips), NSToIntRound(2 * inPixelsToTwips) );
inRenderingContext.FillRect(dividingLine);
}
inRenderingContext.SetColor(inColor);
nsRect dividingLine ( horizIndent, mYDropLoc,
NSToIntRound(50 * inPixelsToTwips), NSToIntRound(2 * inPixelsToTwips) );
inRenderingContext.FillRect(dividingLine);
} // PaintInBetweenDropFeedback

View File

@ -140,7 +140,7 @@ NS_NewXULTreeOuterGroupFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRB
nsXULTreeOuterGroupFrame::nsXULTreeOuterGroupFrame(nsIPresShell* aPresShell, PRBool aIsRoot, nsIBoxLayout* aLayoutManager, PRBool aIsHorizontal)
:nsXULTreeGroupFrame(aPresShell, aIsRoot, aLayoutManager, aIsHorizontal),
mRowGroupInfo(nsnull), mRowHeight(0), mCurrentIndex(0),
mTreeIsSorted(PR_FALSE), mDragOverListener(nsnull),
mTreeIsSorted(PR_FALSE), mDragOverListener(nsnull), mCanDropBetweenRows(PR_TRUE),
mTreeLayoutState(eTreeLayoutNormal), mReflowCallbackPosted(PR_FALSE)
{
}
@ -235,6 +235,17 @@ nsXULTreeOuterGroupFrame::Init(nsIPresContext* aPresContext, nsIContent* aConten
receiver->AddEventListener(NS_ConvertASCIItoUCS2("dragover"), mDragOverListener, PR_FALSE);
}
// our parent is the <tree> tag. check if it has an attribute denying the ability to
// drop between rows and cache it here for the benefit of the rows inside us.
nsCOMPtr<nsIContent> parent;
mContent->GetParent ( *getter_AddRefs(parent) );
if ( parent ) {
nsAutoString attr;
parent->GetAttribute ( kNameSpaceID_None, nsXULAtoms::ddNoDropBetweenRows, attr );
if ( attr.Equals(NS_ConvertASCIItoUCS2("true")) )
mCanDropBetweenRows = PR_FALSE;
}
return rv;
} // Init

View File

@ -186,6 +186,7 @@ public:
NS_IMETHOD InternalPositionChanged(PRBool aUp, PRInt32 aDelta);
PRBool IsTreeSorted ( ) const { return mTreeIsSorted; }
PRBool CanDropBetweenRows ( ) const { return mCanDropBetweenRows; }
nsTreeLayoutState GetTreeLayoutState() { return mTreeLayoutState; }
void SetTreeLayoutState(nsTreeLayoutState aState) { mTreeLayoutState = aState; }
@ -208,6 +209,7 @@ protected:
nscoord mOnePixel;
PRInt32 mCurrentIndex; // Row-based
PRPackedBool mTreeIsSorted;
PRPackedBool mCanDropBetweenRows; // is the user allowed to drop between rows
#if USE_TIMER_TO_DELAY_SCROLLING
PRPackedBool mAutoScrollTimerHasFired;

View File

@ -155,8 +155,10 @@ XUL_ATOM(ddDropOn, "dd-dropon")
XUL_ATOM(ddTriggerRepaintSorted, "dd-triggerrepaintsorted")
XUL_ATOM(ddTriggerRepaintRestore, "dd-triggerrepaintrestore")
XUL_ATOM(ddTriggerRepaint, "dd-triggerrepaint")
XUL_ATOM(ddNoDropBetweenRows, "dd-nodropbetweenrows")
XUL_ATOM(container, "container")
XUL_ATOM(ddDragDropArea, "dragdroparea")
XUL_ATOM(ddDropMarker, ":-moz-drop-marker")
XUL_ATOM(widget, "widget")
XUL_ATOM(window, "window")