/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Netscape Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Original Author: David W. Hyatt (hyatt@netscape.com) * Mike Pinkerton (pinkerton@netscape.com) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the NPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the NPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsCOMPtr.h" #include "nsXULTreeGroupFrame.h" #include "nsCSSFrameConstructor.h" #include "nsBoxLayoutState.h" #include "nsISupportsArray.h" #include "nsTreeItemDragCapturer.h" #include "nsIDOMEventReceiver.h" #include "nsIDOMElement.h" #include "nsIRenderingContext.h" #include "nsIDeviceContext.h" #include "nsXULAtoms.h" #include "nsIViewManager.h" #include "nsINameSpaceManager.h" #include "nsXULTreeOuterGroupFrame.h" #include "nsIDocument.h" #include "nsIBindingManager.h" #include "nsUnicharUtils.h" // // Prototypes // static void LocateIndentationFrame ( nsIPresContext* aPresContext, nsIFrame* aParentFrame, nsIFrame** aResult ) ; // // NS_NewXULTreeGroupFrame // // Creates a new TreeGroup frame // nsresult NS_NewXULTreeGroupFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRBool aIsRoot, nsIBoxLayout* aLayoutManager) { NS_PRECONDITION(aNewFrame, "null OUT ptr"); if (nsnull == aNewFrame) { return NS_ERROR_NULL_POINTER; } nsXULTreeGroupFrame* it = new (aPresShell) nsXULTreeGroupFrame(aPresShell, aIsRoot, aLayoutManager); if (!it) return NS_ERROR_OUT_OF_MEMORY; *aNewFrame = it; return NS_OK; } // NS_NewXULTreeGroupFrame NS_IMETHODIMP_(nsrefcnt) nsXULTreeGroupFrame::AddRef(void) { return NS_OK; } NS_IMETHODIMP_(nsrefcnt) nsXULTreeGroupFrame::Release(void) { return NS_OK; } // // QueryInterface // NS_INTERFACE_MAP_BEGIN(nsXULTreeGroupFrame) NS_INTERFACE_MAP_ENTRY(nsIXULTreeSlice) NS_INTERFACE_MAP_END_INHERITING(nsBoxFrame) // Constructor nsXULTreeGroupFrame::nsXULTreeGroupFrame(nsIPresShell* aPresShell, PRBool aIsRoot, nsIBoxLayout* aLayoutManager) : nsBoxFrame(aPresShell, aIsRoot, aLayoutManager), mFrameConstructor(nsnull), mPresContext(nsnull), mOuterFrame(nsnull), mAvailableHeight(10000), mTopFrame(nsnull), mBottomFrame(nsnull), mLinkupFrame(nsnull), mContentChain(nsnull), mOnScreenRowCount(-1), mYDropLoc(nsTreeItemDragCapturer::kNoDropLoc), mDropOnContainer(PR_FALSE) {} // Destructor nsXULTreeGroupFrame::~nsXULTreeGroupFrame() { nsCOMPtr content; GetContent(getter_AddRefs(content)); nsCOMPtr receiver(do_QueryInterface(content)); // NOTE: the last Remove will delete the drag capturer if ( receiver ) { receiver->RemoveEventListener(NS_LITERAL_STRING("dragover"), mDragCapturer, PR_TRUE); receiver->RemoveEventListener(NS_LITERAL_STRING("dragexit"), mDragCapturer, PR_TRUE); } } // // Init // // Setup event capturers for drag and drop. Our frame's lifetime is bounded by the // lifetime of the content model, so we're guaranteed that the content node won't go away on us. As // a result, our drag capturer can't go away before the frame is deleted. Since the content // node holds owning references to our drag capturer, which we tear down in the dtor, there is no // need to hold an owning ref to it ourselves. // NS_IMETHODIMP nsXULTreeGroupFrame::Init ( nsIPresContext* aPresContext, nsIContent* aContent, nsIFrame* aParent, nsIStyleContext* aContext, nsIFrame* aPrevInFlow ) { nsresult retVal = nsBoxFrame::Init ( aPresContext, aContent, aParent, aContext, aPrevInFlow ); nsCOMPtr receiver(do_QueryInterface(aContent)); if ( NS_SUCCEEDED(retVal) && receiver ) { // register our drag over and exit capturers. These annotate the content object // with enough info to determine where the drop would happen so that JS down the // line can do the right thing. nsresult rv1, rv2; mDragCapturer = new nsTreeItemDragCapturer(this, aPresContext); rv1 = receiver->AddEventListener(NS_LITERAL_STRING("dragover"), mDragCapturer, PR_TRUE); rv2 = receiver->AddEventListener(NS_LITERAL_STRING("dragexit"), mDragCapturer, PR_TRUE); NS_ASSERTION ( NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2), "Couldn't hookup drag capturer on tree" ); } // it's ok to ignore the failure of the event listeners. Nothing bad will come of it. return retVal; } // Init NS_IMETHODIMP nsXULTreeGroupFrame::Redraw(nsBoxLayoutState& aState, const nsRect* aDamageRect, PRBool aImmediate) { return nsBoxFrame::Redraw(aState, aDamageRect, aImmediate); } void nsXULTreeGroupFrame::LocateFrame(nsIFrame* aStartFrame, nsIFrame** aResult) { if (aStartFrame == nsnull) *aResult = mFrames.FirstChild(); else aStartFrame->GetNextSibling(aResult); } NS_IMETHODIMP nsXULTreeGroupFrame::NeedsRecalc() { mOnScreenRowCount = -1; return nsBoxFrame::NeedsRecalc(); } NS_IMETHODIMP nsXULTreeGroupFrame::GetOnScreenRowCount(PRInt32* aCount) { if (mOnScreenRowCount == -1) { mOnScreenRowCount = 0; nsIBox* box = nsnull; GetChildBox(&box); while(box) { PRInt32 count = 0; nsCOMPtr slice(do_QueryInterface(box)); if (slice) { slice->GetOnScreenRowCount(&count); mOnScreenRowCount += count; } box->GetNextBox(&box); } } *aCount = mOnScreenRowCount; return NS_OK; } nsIFrame* nsXULTreeGroupFrame::GetFirstFrame() { LocateFrame(nsnull, &mTopFrame); return mTopFrame; } nsIFrame* nsXULTreeGroupFrame::GetNextFrame(nsIFrame* aPrevFrame) { nsIFrame* result; LocateFrame(aPrevFrame, &result); return result; } nsIFrame* nsXULTreeGroupFrame::GetLastFrame() { return mFrames.LastChild(); } nsIBox* nsXULTreeGroupFrame::GetFirstTreeBox(PRBool* aCreated) { if (aCreated) *aCreated = PR_FALSE; // Clear ourselves out. mLinkupFrame = nsnull; mBottomFrame = mTopFrame; // If we have a frame and no content chain (e.g., unresolved/uncreated content) if (mTopFrame && !mContentChain) { nsCOMPtr box(do_QueryInterface(mTopFrame)); return box; } // See if we have any frame whatsoever. LocateFrame(nsnull, &mTopFrame); mBottomFrame = mTopFrame; nsCOMPtr startContent; if (mContentChain) { nsCOMPtr supports; mContentChain->GetElementAt(0, getter_AddRefs(supports)); nsCOMPtr chainContent = do_QueryInterface(supports); startContent = chainContent; if (mTopFrame) { // We have a content chain. If the top frame is the same as our content // chain, we can go ahead and destroy our content chain and return the // top frame. nsCOMPtr topContent; mTopFrame->GetContent(getter_AddRefs(topContent)); if (chainContent.get() == topContent.get()) { // The two content nodes are the same. Our content chain has // been synched up, and we can now remove our element and // pass the content chain inwards. InitSubContentChain((nsXULTreeGroupFrame*)mTopFrame); nsCOMPtr box(do_QueryInterface(mTopFrame)); return box; } else mLinkupFrame = mTopFrame; // We have some frames that we'll eventually catch up with. // Cache the pointer to the first of these frames, so // we'll know it when we hit it. } } else if (mTopFrame) { nsCOMPtr box(do_QueryInterface(mTopFrame)); return box; } // We don't have a top frame instantiated. Let's // try to make one. // If startContent is initialized, we have a content chain, and // we're using that content node to make our frame. // Otherwise we have nothing, and we should just try to grab the first child. if (!startContent) { nsCOMPtr content(mContent); nsCOMPtr bindingParent; mContent->GetBindingParent(getter_AddRefs(bindingParent)); if (bindingParent) { nsCOMPtr doc; bindingParent->GetDocument(*getter_AddRefs(doc)); nsCOMPtr bindingManager; doc->GetBindingManager(getter_AddRefs(bindingManager)); nsCOMPtr tag; PRInt32 namespaceID; bindingManager->ResolveTag(bindingParent, &namespaceID, getter_AddRefs(tag)); if (tag.get() == nsXULAtoms::tree) content = bindingParent; } PRInt32 childCount; content->ChildCount(childCount); nsCOMPtr childContent; if (childCount > 0) { content->ChildAt(0, *getter_AddRefs(childContent)); startContent = childContent; } } if (startContent) { PRBool isAppend = (mLinkupFrame == nsnull); mFrameConstructor->CreateTreeWidgetContent(mPresContext, this, nsnull, startContent, &mTopFrame, isAppend, PR_FALSE, nsnull); if (aCreated) *aCreated = PR_TRUE; //if (mTopFrame) // mOuterFrame->PostReflowCallback(); mBottomFrame = mTopFrame; nsCOMPtr slice(do_QueryInterface(mTopFrame)); PRBool isGroup = PR_FALSE; if (slice) slice->IsGroupFrame(&isGroup); if (isGroup && mContentChain) { // We have just instantiated a row group, and we have a content chain. This // means we need to potentially pass a sub-content chain to the instantiated // frame, so that it can also sync up with its children. InitSubContentChain((nsXULTreeGroupFrame*)mTopFrame); } SetContentChain(nsnull); nsCOMPtr box(do_QueryInterface(mTopFrame)); return box; } return nsnull; } nsIBox* nsXULTreeGroupFrame::GetNextTreeBox(nsIBox* aBox, PRBool* aCreated) { if (aCreated) *aCreated = PR_FALSE; // We're ultra-cool. We build our frames on the fly. nsIFrame* result; nsIFrame* frame; aBox->GetFrame(&frame); LocateFrame(frame, &result); if (result && (result == mLinkupFrame)) { // We haven't really found a result. We've only found a result if // the linkup frame is really the next frame following the // previous frame. nsCOMPtr prevContent; frame->GetContent(getter_AddRefs(prevContent)); nsCOMPtr linkupContent; mLinkupFrame->GetContent(getter_AddRefs(linkupContent)); PRInt32 i, j; mContent->IndexOf(prevContent, i); mContent->IndexOf(linkupContent, j); if (i+1==j) { // We have found a match and successfully linked back up with our // old frame. mBottomFrame = mLinkupFrame; mLinkupFrame = nsnull; nsCOMPtr box(do_QueryInterface(result)); return box; } else result = nsnull; // No true linkup. We need to make a frame. } if (!result) { // No result found. See if there's a content node that wants a frame. PRInt32 i, childCount; nsCOMPtr prevContent; frame->GetContent(getter_AddRefs(prevContent)); nsCOMPtr parentContent; prevContent->GetParent(*getter_AddRefs(parentContent)); parentContent->IndexOf(prevContent, i); parentContent->ChildCount(childCount); if (i+1 < childCount) { // There is a content node that wants a frame. nsCOMPtr nextContent; parentContent->ChildAt(i+1, *getter_AddRefs(nextContent)); nsIFrame* prevFrame = nsnull; // Default is to append PRBool isAppend = PR_TRUE; if (mLinkupFrame) { // This will be an insertion, since we have frames on the end. aBox->GetFrame(&prevFrame); isAppend = PR_FALSE; } mFrameConstructor->CreateTreeWidgetContent(mPresContext, this, prevFrame, nextContent, &result, isAppend, PR_FALSE, nsnull); if (aCreated) *aCreated = PR_TRUE; //if (result) // mOuterFrame->PostReflowCallback(); } } mBottomFrame = result; nsCOMPtr box(do_QueryInterface(result)); return box; } NS_IMETHODIMP nsXULTreeGroupFrame::TreeInsertFrames(nsIFrame* aPrevFrame, nsIFrame* aFrameList) { // insert the frames to our info list nsBoxLayoutState state(mPresContext); Insert(state, aPrevFrame, aFrameList); mFrames.InsertFrames(nsnull, aPrevFrame, aFrameList); MarkDirtyChildren(state); return NS_OK; } NS_IMETHODIMP nsXULTreeGroupFrame::TreeAppendFrames(nsIFrame* aFrameList) { // append them after nsBoxLayoutState state(mPresContext); Append(state,aFrameList); mFrames.AppendFrames(nsnull, aFrameList); MarkDirtyChildren(state); return NS_OK; } void nsXULTreeGroupFrame::OnContentInserted(nsIPresContext* aPresContext, nsIFrame* aNextSibling, PRInt32 aIndex) { nsIFrame* currFrame = aNextSibling; // this will probably never happen - // if we haven't created the topframe, it doesn't matter if // content was inserted if (mTopFrame == nsnull) return; // if we're inserting the item before the first visible content, // then ignore it because it will end up off-screen // (except of course in the case of the first row, where we're // actually adding visible content) if(aNextSibling == mTopFrame) { if (aIndex > 0) // We aren't at the front, so we have to be offscreen. return; // Just bail. nsCOMPtr content; aNextSibling->GetContent(getter_AddRefs(content)); PRInt32 siblingIndex; mContent->IndexOf(content, siblingIndex); if (siblingIndex == 1 && mOuterFrame->GetYPosition() == 0) // We just inserted an item in front of the first of our children // and we're at the top, such that we have to show the row. // This item is our new visible top row. mTopFrame = nsnull; else // The newly inserted row is offscreen. We can just bail. return; } while (currFrame) { nsIFrame* nextFrame; currFrame->GetNextSibling(&nextFrame); mFrameConstructor->RemoveMappingsForFrameSubtree(aPresContext, currFrame, nsnull); nsBoxLayoutState state(aPresContext); Remove(state, currFrame); mFrames.DestroyFrame(aPresContext, currFrame); currFrame = nextFrame; } nsBoxLayoutState state(aPresContext); MarkDirtyChildren(state); } void nsXULTreeGroupFrame::OnContentRemoved(nsIPresContext* aPresContext, nsIFrame* aChildFrame, PRInt32 aIndex, PRInt32& aOnScreenRowCount) { if (!mFrameConstructor) return; // if we're removing the top row, the new top row is the next row if (mTopFrame && mTopFrame == aChildFrame) mTopFrame->GetNextSibling(&mTopFrame); // Go ahead and delete the frame. nsBoxLayoutState state(aPresContext); if (aChildFrame) { nsCOMPtr slice(do_QueryInterface(aChildFrame)); if (slice) slice->GetOnScreenRowCount(&aOnScreenRowCount); mFrameConstructor->RemoveMappingsForFrameSubtree(aPresContext, aChildFrame, nsnull); Remove(state, aChildFrame); mFrames.DestroyFrame(aPresContext, aChildFrame); } MarkDirtyChildren(state); nsCOMPtr shell; aPresContext->GetShell(getter_AddRefs(shell)); shell->FlushPendingNotifications(PR_FALSE); } PRBool nsXULTreeGroupFrame::ContinueReflow(nscoord height) { if (height <= 0) { nsIFrame* lastChild = GetLastFrame(); nsIFrame* startingPoint = mBottomFrame; if (startingPoint == nsnull) { // We just want to delete everything but the first item. startingPoint = GetFirstFrame(); } if (lastChild != startingPoint) { // We have some hangers on (probably caused by shrinking the size of the window). // Nuke them. nsIFrame* currFrame; startingPoint->GetNextSibling(&currFrame); nsBoxLayoutState state(mPresContext); while (currFrame) { nsIFrame* nextFrame; currFrame->GetNextSibling(&nextFrame); mFrameConstructor->RemoveMappingsForFrameSubtree(mPresContext, currFrame, nsnull); Remove(state, currFrame); mFrames.DestroyFrame(mPresContext, currFrame); currFrame = nextFrame; } MarkDirtyChildren(state); } return PR_FALSE; } else return PR_TRUE; } void nsXULTreeGroupFrame::DestroyRows(PRInt32& aRowsToLose) { // We need to destroy frames until our row count has been properly // reduced. A reflow will then pick up and create the new frames. nsIFrame* childFrame = GetFirstFrame(); while (childFrame && aRowsToLose > 0) { nsCOMPtr slice(do_QueryInterface(childFrame)); if (!slice) continue; PRBool isRowGroup; slice->IsGroupFrame(&isRowGroup); if (isRowGroup) { nsXULTreeGroupFrame* childGroup = (nsXULTreeGroupFrame*)childFrame; childGroup->DestroyRows(aRowsToLose); if (aRowsToLose == 0) { nsIBox* box = nsnull; childGroup->GetChildBox(&box); if (box) // Still have kids. Just return. return; } } else { // Lost a row. aRowsToLose--; } nsIFrame* nextFrame = GetNextFrame(childFrame); mFrameConstructor->RemoveMappingsForFrameSubtree(mPresContext, childFrame, nsnull); nsBoxLayoutState state(mPresContext); Remove(state, childFrame); mFrames.DestroyFrame(mPresContext, childFrame); MarkDirtyChildren(state); mTopFrame = childFrame = nextFrame; } } void nsXULTreeGroupFrame::ReverseDestroyRows(PRInt32& aRowsToLose) { // We need to destroy frames until our row count has been properly // reduced. A reflow will then pick up and create the new frames. nsIFrame* childFrame = GetLastFrame(); while (childFrame && aRowsToLose > 0) { nsCOMPtr slice(do_QueryInterface(childFrame)); if (!slice) continue; PRBool isRowGroup; slice->IsGroupFrame(&isRowGroup); if (isRowGroup) { nsXULTreeGroupFrame* childGroup = (nsXULTreeGroupFrame*)childFrame; childGroup->ReverseDestroyRows(aRowsToLose); if (aRowsToLose == 0) { nsIBox* box = nsnull; childGroup->GetChildBox(&box); if (box) // Still have kids. Just return. return; } } else { // Lost a row. aRowsToLose--; } nsIFrame* prevFrame; prevFrame = mFrames.GetPrevSiblingFor(childFrame); mFrameConstructor->RemoveMappingsForFrameSubtree(mPresContext, childFrame, nsnull); nsBoxLayoutState state(mPresContext); Remove(state, childFrame); mFrames.DestroyFrame(mPresContext, childFrame); MarkDirtyChildren(state); mBottomFrame = childFrame = prevFrame; } } void nsXULTreeGroupFrame::GetFirstRowContent(nsIContent** aResult) { *aResult = nsnull; nsIFrame* kid = GetFirstFrame(); while (kid) { nsCOMPtr slice(do_QueryInterface(kid)); if (!slice) continue; PRBool isRowGroup; slice->IsGroupFrame(&isRowGroup); if (isRowGroup) { ((nsXULTreeGroupFrame*)kid)->GetFirstRowContent(aResult); if (*aResult) return; } else { kid->GetContent(aResult); // The ADDREF happens here. return; } kid = GetNextFrame(kid); } } void nsXULTreeGroupFrame::SetContentChain(nsISupportsArray* aContentChain) { NS_IF_RELEASE(mContentChain); mContentChain = aContentChain; NS_IF_ADDREF(mContentChain); } void nsXULTreeGroupFrame::InitSubContentChain(nsXULTreeGroupFrame* aRowGroupFrame) { if (mContentChain) { mContentChain->RemoveElementAt(0); PRUint32 chainSize; mContentChain->Count(&chainSize); if (chainSize > 0 && aRowGroupFrame) { aRowGroupFrame->SetContentChain(mContentChain); } // The chain is dead. Long live the chain. SetContentChain(nsnull); } } #ifdef XP_MAC #pragma mark - #endif // // [ Drag And Drop Methods ] // // These will hopefully all go away (and their 20K of extra code) when we convert over // to using the style system to do drop feedback. // // // ForceDrawFrame // // Force this frame to drop and give me twenty. // NOTE: try to consolodate with the version in the toolbar code. // void nsXULTreeGroupFrame :: ForceDrawFrame(nsIPresContext* aPresContext, nsIFrame * aFrame) { if (aFrame == nsnull) { return; } nsRect rect; nsIView * view; nsPoint pnt; aFrame->GetOffsetFromView(aPresContext, pnt, &view); aFrame->GetRect(rect); rect.x = pnt.x; rect.y = pnt.y; if (view) { nsCOMPtr viewMgr; view->GetViewManager(*getter_AddRefs(viewMgr)); if (viewMgr) viewMgr->UpdateView(view, rect, NS_VMREFRESH_IMMEDIATE); } } // ForceDrawFrame // // AttributeChanged // // Track several attributes set by the d&d drop feedback tracking mechanism. The first // is the "dd-triggerrepaint" attribute so JS can trigger a repaint when it // needs up update the drop feedback. The second is the x (or y, if bar is vertical) // coordinate of where the drop feedback bar should be drawn. // NS_IMETHODIMP nsXULTreeGroupFrame::AttributeChanged(nsIPresContext* aPresContext, nsIContent* aChild, PRInt32 aNameSpaceID, nsIAtom* aAttribute, PRInt32 aModType, PRInt32 aHint) { nsresult rv = NS_OK; PRInt32 ignore; if ( aAttribute == nsXULAtoms::ddTriggerRepaint ) ForceDrawFrame ( aPresContext, this ); else if ( aAttribute == nsXULAtoms::ddTriggerRepaintRestore ) { // Repaint the entire tree with no special attributes set. ForceDrawFrame ( aPresContext, NS_REINTERPRET_CAST(nsIFrame*, mOuterFrame) ); } else if ( aAttribute == nsXULAtoms::ddDropLocationCoord ) { nsAutoString attribute; aChild->GetAttr ( kNameSpaceID_None, aAttribute, attribute ); mYDropLoc = attribute.ToInteger(&ignore); } else if ( aAttribute == nsXULAtoms::ddDropOn ) { nsAutoString attribute; aChild->GetAttr ( kNameSpaceID_None, aAttribute, attribute ); ToLowerCase(attribute); mDropOnContainer = attribute.Equals(NS_LITERAL_STRING("true")); } else rv = nsBoxFrame::AttributeChanged ( aPresContext, aChild, aNameSpaceID, aAttribute, aModType, aHint ); return rv; } // AttributeChanged // // Paint // // Used to draw the drop feedback based on attributes set by the drag event capturer // NS_IMETHODIMP nsXULTreeGroupFrame :: Paint ( nsIPresContext* aPresContext, nsIRenderingContext& aRenderingContext, const nsRect& aDirtyRect, nsFramePaintLayer aWhichLayer, PRUint32 aFlags) { nsresult res = NS_OK; res = nsBoxFrame::Paint ( aPresContext, aRenderingContext, aDirtyRect, aWhichLayer ); if ( (aWhichLayer == eFramePaintLayer_Content) && (mYDropLoc != nsTreeItemDragCapturer::kNoDropLoc || mDropOnContainer) ) PaintDropFeedback ( aPresContext, aRenderingContext, PR_FALSE ); return res; } // Paint // // PaintDropFeedback // // Depending on the various flags, dispatch to the appropriate method for drawing // drop feedback. // void nsXULTreeGroupFrame :: PaintDropFeedback ( nsIPresContext* aPresContext, nsIRenderingContext& aRenderingContext, PRBool aPaintSorted ) { // lookup the drop marker color. default to black if not found. 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. float p2t = 20.0; nsCOMPtr dc; aRenderingContext.GetDeviceContext ( *getter_AddRefs(dc) ); if ( dc ) dc->GetDevUnitsToTwips ( p2t ); if ( aPaintSorted ) PaintSortedDropFeedback ( color, aRenderingContext, p2t ); else if ( !mOuterFrame->IsTreeSorted() ) { // draw different drop feedback depending on if we are dropping on the // container or above/below it if ( !mDropOnContainer ) PaintInBetweenDropFeedback ( color, aRenderingContext, aPresContext, p2t ); else PaintOnContainerDropFeedback ( color, aRenderingContext, aPresContext, p2t ); } // else tree not sorted } // PaintDropFeedback // // PaintSortedDropFeedback // // Draws the drop feedback for when the tree is sorted, so line-item drop feedback is // not appliable. // void nsXULTreeGroupFrame :: PaintSortedDropFeedback ( nscolor inColor, nsIRenderingContext& inRenderingContext, float & inPixelsToTwips ) { // two pixels wide const PRInt32 borderWidth = NSToIntRound(2 * inPixelsToTwips); nsRect top ( 0, 0, mRect.width, borderWidth ); nsRect left ( 0, 0, borderWidth, mRect.height ); nsRect right ( mRect.width - borderWidth, 0, borderWidth, mRect.height ); nsRect bottom ( 0, mRect.height - borderWidth, mRect.width, borderWidth ); inRenderingContext.SetColor(inColor); inRenderingContext.FillRect ( top ); inRenderingContext.FillRect ( left ); inRenderingContext.FillRect ( bottom ); inRenderingContext.FillRect ( right ); } // PaintSortedDropFeedback // // PaintOnContainerDropFeedback // // 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 ) { 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 ); } } // PaintOnContainerDropFeedback // // PaintInBetweenDropFeedback // // 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 ) { 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); } } // PaintInBetweenDropFeedback // // FindIndentation // // Compute horizontal offset for dividing line by finding a treeindentation tag // and using its right coordinate. // // NOTE: We assume this indentation tag is in the first column. // NOTE: We aren't caching this value because of space reasons.... // PRInt32 nsXULTreeGroupFrame :: FindIndentation ( nsIPresContext* inPresContext, nsIFrame* inStartFrame ) const { PRInt32 indentInTwips = 0; if ( !inStartFrame ) return 0; nsIFrame* treeRowFrame; inStartFrame->FirstChild ( inPresContext, nsnull, &treeRowFrame ); if ( !treeRowFrame ) return 0; nsIFrame* treeCellFrame; treeRowFrame->FirstChild ( inPresContext, nsnull, &treeCellFrame ); if ( !treeCellFrame ) return 0; nsIFrame* treeIndentFrame = nsnull; LocateIndentationFrame ( inPresContext, treeCellFrame, &treeIndentFrame ); if ( treeIndentFrame ) { nsRect treeIndentBounds; treeIndentFrame->GetRect ( treeIndentBounds ); indentInTwips = treeIndentBounds.x + treeIndentBounds.width; } return indentInTwips; } // FindIndentation // // LocateIndentationFrame // // Recursively locate the tag // void LocateIndentationFrame ( nsIPresContext* aPresContext, nsIFrame* aParentFrame, nsIFrame** aResult) { // Check ourselves. *aResult = nsnull; nsCOMPtr content; aParentFrame->GetContent(getter_AddRefs(content)); nsCOMPtr tagName; content->GetTag ( *getter_AddRefs(tagName) ); if ( tagName.get() == nsXULAtoms::treeindentation ) { *aResult = aParentFrame; return; } // Check our kids. nsIFrame* currFrame; aParentFrame->FirstChild(aPresContext, nsnull, &currFrame); while (currFrame) { LocateIndentationFrame(aPresContext, currFrame, aResult); if (*aResult) return; currFrame->GetNextSibling(&currFrame); } } // // FindFirstChildTreeItemFrame // // Locates the first in our child list. Assumes that we are a container and that // the children are visible. // void nsXULTreeGroupFrame :: FindFirstChildTreeItemFrame ( nsIPresContext* inPresContext, nsIFrame** outChild ) const { *outChild = nsnull; // first find the tag in our child list. nsIFrame* currChildFrame = nsnull; FirstChild ( inPresContext, nsnull, &currChildFrame ); while ( currChildFrame ) { nsCOMPtr content; currChildFrame->GetContent ( getter_AddRefs(content) ); nsCOMPtr tagName; content->GetTag ( *getter_AddRefs(tagName) ); if ( tagName.get() == nsXULAtoms::treechildren ) break; currChildFrame->GetNextSibling ( &currChildFrame ); } // foreach child of the treeItem //NS_ASSERTION ( currChildFrame, "Can't find " ); // |currChildFrame| now holds the correct frame if we found it if ( currChildFrame ) currChildFrame->FirstChild ( inPresContext, nsnull, outChild ); } // FindFirstChildTreeItemFrame // // IsOpenContainer // // Determine if a node is both a container and open // PRBool nsXULTreeGroupFrame :: IsOpenContainer ( ) const { PRBool isOpenContainer = PR_FALSE; nsCOMPtr me ( do_QueryInterface(mContent) ); if ( me ) { nsAutoString isContainer, isOpen; me->GetAttribute(NS_LITERAL_STRING("container"), isContainer); me->GetAttribute(NS_LITERAL_STRING("open"), isOpen); isOpenContainer = (isContainer.Equals(NS_LITERAL_STRING("true")) && isOpen.Equals(NS_LITERAL_STRING("true"))); } return isOpenContainer; } // IsOpenContainer // // GetColorFromStyleContext // // A little helper to root out a color from the current style context. Returns // the given default color if we can't find it, for whatever reason. // nscolor nsXULTreeGroupFrame :: GetColorFromStyleContext ( nsIPresContext* inPresContext, nsIAtom* inAtom, nscolor inDefaultColor ) { nscolor retColor = inDefaultColor; // go looking for the psuedo-style. We have decided not to cache this for space reasons. nsCOMPtr markerStyle; inPresContext->ProbePseudoStyleContextFor(mContent, inAtom, mStyleContext, PR_FALSE, getter_AddRefs(markerStyle)); // dig out the color we want. if ( markerStyle ) { const nsStyleColor* styleColor = NS_STATIC_CAST(const nsStyleColor*, markerStyle->GetStyleData(eStyleStruct_Color)); retColor = styleColor->mColor; } return retColor; } // GetColorFromStyleContext