/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ #include "nsFrame.h" #include "nsFrameList.h" #include "nsLineLayout.h" #include "nsIContent.h" #include "nsIAtom.h" #include "nsString.h" #include "nsIStyleContext.h" #include "nsIView.h" #include "nsIViewManager.h" #include "nsIPresContext.h" #include "nsCRT.h" #include "nsGUIEvent.h" #include "nsDOMEvent.h" #include "nsStyleConsts.h" #include "nsIPresShell.h" #include "prlog.h" #include "prprf.h" #include #include "nsIPtr.h" #include "nsISizeOfHandler.h" #include "nsIDOMText.h" #include "nsDocument.h" #include "nsIDeviceContext.h" #include "nsIPresShell.h" #include "nsHTMLIIDs.h" #include "nsIEventStateManager.h" #include "nsIFocusTracker.h" #include "nsHTMLParts.h" #include "nsLayoutAtoms.h" // Some Misc #defines #define SELECTION_DEBUG 0 #define FORCE_SELECTION_UPDATE 1 #define TRACKER_DEBUG 0 #define CALC_DEBUG 0 // Tracker Data #define kInsertInRemoveList 0 #define kInsertInAddList 1 // Kludged Content stuff nsIFrame * fFrameArray[1024]; nsIContent * fContentArray[1024]; PRInt32 fMax = -1; nsIContent * fTrackerContentArrayRemoveList[1024]; PRInt32 fTrackerRemoveListMax = 0; nsIContent * fTrackerContentArrayAddList[1024]; PRInt32 fTrackerAddListMax = 0; nsIDocument *gDoc = nsnull; // [HACK] Foward Declarations void ForceDrawFrame(nsFrame * aFrame); static void RefreshContentFrames(nsIPresContext& aPresContext, nsIContent * aStartContent, nsIContent * aEndContent); PRBool nsFrame::mDoingSelection = PR_FALSE; PRBool nsFrame::mDidDrag = PR_FALSE; //---------------------------------------------------------------------- static PRBool gShowFrameBorders = PR_FALSE; NS_LAYOUT void nsIFrame::ShowFrameBorders(PRBool aEnable) { gShowFrameBorders = aEnable; } NS_LAYOUT PRBool nsIFrame::GetShowFrameBorders() { return gShowFrameBorders; } /** * Note: the log module is created during library initialization which * means that you cannot perform logging before then. */ PRLogModuleInfo* nsIFrame::gLogModule = PR_NewLogModule("frame"); static PRLogModuleInfo* gFrameVerifyTreeLogModuleInfo; static PRBool gFrameVerifyTreeEnable = PRBool(0x55); NS_LAYOUT PRBool nsIFrame::GetVerifyTreeEnable() { #ifdef NS_DEBUG if (gFrameVerifyTreeEnable == PRBool(0x55)) { if (nsnull == gFrameVerifyTreeLogModuleInfo) { gFrameVerifyTreeLogModuleInfo = PR_NewLogModule("frameverifytree"); gFrameVerifyTreeEnable = 0 != gFrameVerifyTreeLogModuleInfo->level; printf("Note: frameverifytree is %sabled\n", gFrameVerifyTreeEnable ? "en" : "dis"); } } #endif return gFrameVerifyTreeEnable; } NS_LAYOUT void nsIFrame::SetVerifyTreeEnable(PRBool aEnabled) { gFrameVerifyTreeEnable = aEnabled; } NS_LAYOUT PRLogModuleInfo* nsIFrame::GetLogModuleInfo() { return gLogModule; } //---------------------------------------------------------------------- static NS_DEFINE_IID(kIFrameIID, NS_IFRAME_IID); static NS_DEFINE_IID(kIFocusTracker, NS_IFOCUSTRACKER_IID); nsresult NS_NewEmptyFrame(nsIFrame** aInstancePtrResult) { NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr"); if (nsnull == aInstancePtrResult) { return NS_ERROR_NULL_POINTER; } nsIFrame* it = new nsFrame; if (nsnull == it) { return NS_ERROR_OUT_OF_MEMORY; } *aInstancePtrResult = it; return NS_OK; } void* nsFrame::operator new(size_t size) { void* result = new char[size]; nsCRT::zero(result, size); return result; } nsFrame::nsFrame() { mState = NS_FRAME_FIRST_REFLOW | NS_FRAME_SYNC_FRAME_AND_VIEW; mSelected = PR_FALSE; } nsFrame::~nsFrame() { NS_IF_RELEASE(mContent); NS_IF_RELEASE(mStyleContext); if (nsnull != mView) { // Break association between view and frame mView->Destroy(); mView = nsnull; } } ///////////////////////////////////////////////////////////////////////////// // nsISupports nsresult nsFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) { if (NULL == aInstancePtr) { return NS_ERROR_NULL_POINTER; } static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); static NS_DEFINE_IID(kClassIID, kIFrameIID); if (aIID.Equals(kIHTMLReflowIID)) { *aInstancePtr = (void*)(nsIHTMLReflow*)this; return NS_OK; } else if (aIID.Equals(kClassIID) || aIID.Equals(kISupportsIID)) { *aInstancePtr = (void*)this; return NS_OK; } return NS_NOINTERFACE; } nsrefcnt nsFrame::AddRef(void) { NS_WARNING("not supported for frames"); return 1; } nsrefcnt nsFrame::Release(void) { NS_WARNING("not supported for frames"); return 1; } ///////////////////////////////////////////////////////////////////////////// // nsIFrame NS_IMETHODIMP nsFrame::Init(nsIPresContext& aPresContext, nsIContent* aContent, nsIFrame* aParent, nsIStyleContext* aContext) { mContent = aContent; NS_IF_ADDREF(mContent); mParent = aParent; return SetStyleContext(&aPresContext, aContext); } NS_IMETHODIMP nsFrame::SetInitialChildList(nsIPresContext& aPresContext, nsIAtom* aListName, nsIFrame* aChildList) { // XXX This shouldn't be getting called at all, but currently is for backwards // compatility reasons... #if 0 NS_ERROR("not a container"); return NS_ERROR_UNEXPECTED; #else NS_ASSERTION(nsnull == aChildList, "not a container"); return NS_OK; #endif } NS_IMETHODIMP nsFrame::AppendFrames(nsIPresContext& aPresContext, nsIPresShell& aPresShell, nsIAtom* aListName, nsIFrame* aFrameList) { #if 0 return NS_ERROR_UNEXPECTED; #else #ifdef NS_DEBUG nsFrameList tmp(aFrameList); tmp.VerifyParent(this); #endif // XXX temporary code until frame containers stop using the old // reflow command api's nsIReflowCommand* reflowCmd = nsnull; nsresult rv; rv = NS_NewHTMLReflowCommand(&reflowCmd, this, nsIReflowCommand::FrameAppended, aFrameList); if (NS_SUCCEEDED(rv)) { if (nsnull != aListName) { reflowCmd->SetChildListName(aListName); } aPresShell.AppendReflowCommand(reflowCmd); NS_RELEASE(reflowCmd); } return rv; #endif } NS_IMETHODIMP nsFrame::InsertFrames(nsIPresContext& aPresContext, nsIPresShell& aPresShell, nsIAtom* aListName, nsIFrame* aPrevFrame, nsIFrame* aFrameList) { #if 0 return NS_ERROR_UNEXPECTED; #else #ifdef NS_DEBUG nsFrameList tmp(aFrameList); tmp.VerifyParent(this); #endif // XXX temporary code until frame containers stop using the old // reflow command api's // By default, the reflow command is aimed at the first-in-flow. We // map that to the flow block that contains aPrevFrame for // compatability. nsIFrame* target = this; if (nsnull != aPrevFrame) { aPrevFrame->GetParent(target); } nsIReflowCommand* reflowCmd = nsnull; nsresult rv; rv = NS_NewHTMLReflowCommand(&reflowCmd, target, aFrameList, aPrevFrame); if (NS_SUCCEEDED(rv)) { if (nsnull != aListName) { reflowCmd->SetChildListName(aListName); } aPresShell.AppendReflowCommand(reflowCmd); NS_RELEASE(reflowCmd); } return rv; #endif } NS_IMETHODIMP nsFrame::RemoveFrame(nsIPresContext& aPresContext, nsIPresShell& aPresShell, nsIAtom* aListName, nsIFrame* aOldFrame) { #if 0 return NS_ERROR_UNEXPECTED; #else #ifdef NS_DEBUG nsIFrame* parent; aOldFrame->GetParent(parent); NS_ASSERTION(parent == this, "bad child parent"); #endif // XXX temporary code until frame containers stop using the old // reflow command api's // By default, the reflow command is aimed at the first-in-flow. We // map that to the flow block that contains aPrevFrame for // compatability. nsIFrame* target = this; if (nsnull != aOldFrame) { aOldFrame->GetParent(target); } nsIReflowCommand* reflowCmd = nsnull; nsresult rv; rv = NS_NewHTMLReflowCommand(&reflowCmd, target, nsIReflowCommand::FrameRemoved, aOldFrame); if (NS_SUCCEEDED(rv)) { if (nsnull != aListName) { reflowCmd->SetChildListName(aListName); } aPresShell.AppendReflowCommand(reflowCmd); NS_RELEASE(reflowCmd); } return rv; #endif } NS_IMETHODIMP nsFrame::DeleteFrame(nsIPresContext& aPresContext) { if (mState & NS_FRAME_EXTERNAL_REFERENCE) { nsIPresShell *shell = aPresContext.GetShell(); if (nsnull != shell) { shell->ClearFrameRefs(this); NS_RELEASE(shell); } } //XXX Why is this done in nsFrame instead of some frame class // that actually loads images? aPresContext.StopLoadImage(this); //Set to prevent event dispatch during destruct if (nsnull != mView) { mView->SetClientData(nsnull); } delete this; return NS_OK; } NS_IMETHODIMP nsFrame::GetContent(nsIContent*& aContent) const { NS_IF_ADDREF(mContent); aContent = mContent; return NS_OK; } NS_IMETHODIMP nsFrame::GetOffsets(PRInt32 &aStart, PRInt32 &aEnd) const { aStart = 0; aEnd = 0; return NS_OK; } NS_IMETHODIMP nsFrame::GetStyleContext(nsIStyleContext*& aStyleContext) const { NS_ASSERTION(nsnull != mStyleContext, "frame should always have style context"); NS_IF_ADDREF(mStyleContext); aStyleContext = mStyleContext; return NS_OK; } NS_IMETHODIMP nsFrame::SetStyleContext(nsIPresContext* aPresContext,nsIStyleContext* aContext) { NS_PRECONDITION(0 == (mState & NS_FRAME_IN_REFLOW), "Shouldn't set style context during reflow"); NS_PRECONDITION(nsnull != aContext, "null ptr"); if (aContext != mStyleContext) { NS_IF_RELEASE(mStyleContext); if (nsnull != aContext) { mStyleContext = aContext; NS_ADDREF(aContext); DidSetStyleContext(aPresContext); } } return NS_OK; } // Subclass hook for style post processing NS_IMETHODIMP nsFrame::DidSetStyleContext(nsIPresContext* aPresContext) { return NS_OK; } NS_IMETHODIMP nsFrame::GetStyleData(nsStyleStructID aSID, const nsStyleStruct*& aStyleStruct) const { NS_ASSERTION(mStyleContext!=nsnull,"null style context"); if (mStyleContext) { aStyleStruct = mStyleContext->GetStyleData(aSID); } else { aStyleStruct = nsnull; } return NS_OK; } NS_IMETHODIMP nsFrame::ReResolveStyleContext(nsIPresContext* aPresContext, nsIStyleContext* aParentContext) { // XXX TURN THIS ON NS_PRECONDITION(0 == (mState & NS_FRAME_IN_REFLOW), "Shouldn't set style context during reflow"); NS_ASSERTION(nsnull != mStyleContext, "null style context"); if (nsnull != mStyleContext) { nsIAtom* pseudoTag = nsnull; mStyleContext->GetPseudoType(pseudoTag); nsIStyleContext* newContext; if (nsnull != pseudoTag) { newContext = aPresContext->ResolvePseudoStyleContextFor(mContent, pseudoTag, aParentContext); } else { newContext = aPresContext->ResolveStyleContextFor(mContent, aParentContext); } NS_ASSERTION(nsnull != newContext, "failed to get new style context"); if (nsnull != newContext) { if (newContext != mStyleContext) { NS_RELEASE(mStyleContext); mStyleContext = newContext; DidSetStyleContext(aPresContext); } else { NS_RELEASE(newContext); mStyleContext->RemapStyle(aPresContext); } } } return NS_OK; } // Geometric parent member functions NS_IMETHODIMP nsFrame::GetParent(nsIFrame*& aParent) const { aParent = mParent; return NS_OK; } NS_IMETHODIMP nsFrame::SetParent(const nsIFrame* aParent) { mParent = (nsIFrame*)aParent; return NS_OK; } // Bounding rect member functions NS_IMETHODIMP nsFrame::GetRect(nsRect& aRect) const { aRect = mRect; return NS_OK; } NS_IMETHODIMP nsFrame::GetOrigin(nsPoint& aPoint) const { aPoint.x = mRect.x; aPoint.y = mRect.y; return NS_OK; } NS_IMETHODIMP nsFrame::GetSize(nsSize& aSize) const { aSize.width = mRect.width; aSize.height = mRect.height; return NS_OK; } NS_IMETHODIMP nsFrame::SetRect(const nsRect& aRect) { MoveTo(aRect.x, aRect.y); SizeTo(aRect.width, aRect.height); return NS_OK; } NS_IMETHODIMP nsFrame::MoveTo(nscoord aX, nscoord aY) { mRect.x = aX; mRect.y = aY; if (nsnull != mView) { // If we should keep the view position and size in sync with the frame // then position the view. Don't do this if we're in the middle of reflow. // Instead wait until the DidReflow() notification if (NS_FRAME_SYNC_FRAME_AND_VIEW == (mState & (NS_FRAME_IN_REFLOW | NS_FRAME_SYNC_FRAME_AND_VIEW))) { // Position view relative to its parent, not relative to our // parent frame (our parent frame may not have a view). nsIView* parentWithView; nsPoint origin; GetOffsetFromView(origin, parentWithView); nsIViewManager *vm; mView->GetViewManager(vm); vm->MoveViewTo(mView, origin.x, origin.y); NS_RELEASE(vm); } } return NS_OK; } NS_IMETHODIMP nsFrame::SizeTo(nscoord aWidth, nscoord aHeight) { mRect.width = aWidth; mRect.height = aHeight; // Let the view know if (nsnull != mView) { // If we should keep the view position and size in sync with the frame // then resize the view. Don't do this if we're in the middle of reflow. // Instead wait until the DidReflow() notification if (NS_FRAME_SYNC_FRAME_AND_VIEW == (mState & (NS_FRAME_IN_REFLOW | NS_FRAME_SYNC_FRAME_AND_VIEW))) { // Resize the view to be the same size as the frame nsIViewManager *vm; mView->GetViewManager(vm); vm->ResizeView(mView, aWidth, aHeight); NS_RELEASE(vm); } } return NS_OK; } // Child frame enumeration NS_IMETHODIMP nsFrame::GetAdditionalChildListName(PRInt32 aIndex, nsIAtom*& aListName) const { NS_PRECONDITION(aIndex >= 0, "invalid index number"); aListName = nsnull; return aIndex < 0 ? NS_ERROR_INVALID_ARG : NS_OK; } NS_IMETHODIMP nsFrame::FirstChild(nsIAtom* aListName, nsIFrame*& aFirstChild) const { aFirstChild = nsnull; return nsnull == aListName ? NS_OK : NS_ERROR_INVALID_ARG; } PRBool nsFrame::DisplaySelection(nsIPresContext& aPresContext, PRBool isOkToTurnOn) { PRBool result = PR_FALSE; nsIPresShell *shell = aPresContext.GetShell(); if (nsnull != shell) { nsIDocument *doc = shell->GetDocument(); if (nsnull != doc) { result = doc->GetDisplaySelection(); if (isOkToTurnOn && !result) { doc->SetDisplaySelection(PR_TRUE); result = PR_TRUE; } NS_RELEASE(doc); } NS_RELEASE(shell); } return result; } NS_IMETHODIMP nsFrame::Paint(nsIPresContext& aPresContext, nsIRenderingContext& aRenderingContext, const nsRect& aDirtyRect, nsFramePaintLayer aWhichLayer) { if (eFramePaintLayer_Overlay == aWhichLayer) { if (DisplaySelection(aPresContext) == PR_FALSE) return NS_OK; // Get Content nsIContent* content; nsresult rv = GetContent(content); if (NS_FAILED(rv) || (nsnull == content)) { return rv; } PRInt32 n; content->ChildCount(n); if ((n == 0) && mSelected) { nsRect rect; GetRect(rect); rect.width--; rect.height--; aRenderingContext.SetColor(NS_RGB(0,0,255)); aRenderingContext.DrawRect(rect); aRenderingContext.DrawLine(rect.x, rect.y, rect.XMost(), rect.YMost()); aRenderingContext.DrawLine(rect.x, rect.YMost(), rect.XMost(), rect.y); } NS_RELEASE(content); } return NS_OK; } /** * */ NS_IMETHODIMP nsFrame::HandleEvent(nsIPresContext& aPresContext, nsGUIEvent* aEvent, nsEventStatus& aEventStatus) { if (nsEventStatus_eConsumeNoDefault == aEventStatus) { return NS_OK; } if (DisplaySelection(aPresContext) == PR_FALSE) { if (aEvent->message != NS_MOUSE_LEFT_BUTTON_DOWN) { return NS_OK; } } if(nsEventStatus_eConsumeNoDefault != aEventStatus) { if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN) { } else if (aEvent->message == NS_MOUSE_MOVE && mDoingSelection || aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) { // no-op } else { return NS_OK; } if (SELECTION_DEBUG) printf("Message: %d-------------------------------------------------------------\n",aEvent->message); if (aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) { if (mDoingSelection) { HandleRelease(aPresContext, aEvent, aEventStatus); } } else if (aEvent->message == NS_MOUSE_MOVE) { mDidDrag = PR_TRUE; HandleDrag(aPresContext, aEvent, aEventStatus); //DEBUG MJUDGE DEBUG MESSAGE FOR DRAGGING FROM SELECTION } else if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN) { HandlePress(aPresContext, aEvent, aEventStatus); } } return NS_OK; } /** * Handles the Mouse Press Event for the frame */ NS_IMETHODIMP nsFrame::HandlePress(nsIPresContext& aPresContext, nsGUIEvent* aEvent, nsEventStatus& aEventStatus) { if (DisplaySelection(aPresContext, PR_TRUE) == PR_FALSE) { aEventStatus = nsEventStatus_eIgnore; return NS_OK; } mDoingSelection = PR_TRUE; mDidDrag = PR_FALSE; nsIPresShell *shell = aPresContext.GetShell(); if (shell){ nsIRenderingContext * acx; if (NS_SUCCEEDED(shell->CreateRenderingContext(this, acx))){ PRInt32 startPos = 0; PRUint32 contentOffset = 0; if (NS_SUCCEEDED(GetPosition(aPresContext, acx, aEvent, this, contentOffset, startPos))){ nsISelection *selection = nsnull; if (NS_SUCCEEDED(shell->GetSelection(&selection))){ nsIFocusTracker *tracker; if (NS_SUCCEEDED(shell->QueryInterface(kIFocusTracker,(void **)&tracker))){ selection->TakeFocus(tracker, this, startPos, contentOffset, PR_FALSE); NS_RELEASE(tracker); } NS_RELEASE(selection); } //no release } NS_RELEASE(acx); } NS_RELEASE(shell); } #if 0 //DEBUG MJUDGE nsFrame * currentFrame = this; nsIPresShell * shell = aPresContext.GetShell(); nsMouseEvent * mouseEvent = (nsMouseEvent *)aEvent; nsIRenderingContext * acx; gDoc = shell->GetDocument(); nsISelection * selection; gDoc->GetSelection(selection); shell->CreateRenderingContext(this, acx); mSelectionRange = selection->GetRange(); PRUint32 actualOffset = 0; mDoingSelection = PR_TRUE; mDidDrag = PR_FALSE; mCurrentFrame = currentFrame; GetPosition(aPresContext, acx, aEvent, currentFrame, actualOffset, mStartPos); // Click count is 1 nsIContent * newContent; currentFrame->GetContent(newContent); mCurrentFrame = currentFrame; if (mStartSelectionPoint == nsnull) { mStartSelectionPoint = new nsSelectionPoint(nsnull, -1, PR_TRUE); } if (mEndSelectionPoint == nsnull) { mEndSelectionPoint = new nsSelectionPoint(nsnull, -1, PR_TRUE); } mSelectionRange->GetStartPoint(mStartSelectionPoint); mSelectionRange->GetEndPoint(mEndSelectionPoint); resetContentTrackers(); // Get the Current Start and End Content Pointers nsIContent * selStartContent = mSelectionRange->GetStartContent(); // ref counted nsIContent * selEndContent = mSelectionRange->GetEndContent(); // ref counted if (SELECTION_DEBUG) printf("****** Shift[%s]\n", (mouseEvent->isShift?"Down":"Up")); PRBool inRange = gDoc->IsInRange(selStartContent, selEndContent, newContent); if (inRange) { //resetContentTrackers(); if (TRACKER_DEBUG) printf("Adding split range to removed selection. Shift[%s]\n", (mouseEvent->isShift?"Down":"Up")); if (mouseEvent->isShift) { if (newContent == selStartContent && newContent == selEndContent) { addRangeToSelectionTrackers(newContent, newContent, kInsertInAddList); } else { if (selStartContent == newContent) { // Trackers just do painting so add all the content nodes in the remove list // even though you might think you shouldn't put the start content node there addRangeToSelectionTrackers(selStartContent, selEndContent, kInsertInRemoveList); } else if (selEndContent == newContent) { // just repaint the end content node addRangeToSelectionTrackers(selEndContent, selEndContent, kInsertInAddList); } else { if (gDoc->IsBefore(newContent, selEndContent)) { addRangeToSelectionTrackers(newContent, selEndContent, kInsertInRemoveList); } else { addRangeToSelectionTrackers(selEndContent, newContent, kInsertInRemoveList); } } } mEndSelectionPoint->SetPoint(newContent, mStartPos, PR_FALSE); } else { addRangeToSelectionTrackers(selStartContent, selEndContent, kInsertInRemoveList); // removed from selection mStartSelectionPoint->SetPoint(newContent, mStartPos, PR_TRUE); mEndSelectionPoint->SetPoint(newContent, mStartPos, PR_TRUE); addRangeToSelectionTrackers(newContent, newContent, kInsertInAddList); // add to selection } } else if (gDoc->IsBefore(newContent, selStartContent)) { if (mouseEvent->isShift) { if (mStartSelectionPoint->IsAnchor()) { if (SELECTION_DEBUG) printf("New Content is before, Start will now be end\n"); addRangeToSelectionTrackers(selStartContent, selEndContent, kInsertInRemoveList); // removed from selection mEndSelectionPoint->SetPoint(selStartContent, mStartSelectionPoint->GetOffset(), PR_TRUE); mStartSelectionPoint->SetPoint(newContent, mStartPos, PR_FALSE); // End Point has changed nsIContent * endcontent = mEndSelectionPoint->GetContent(); // ref counted addRangeToSelectionTrackers(newContent, endcontent, kInsertInAddList); // add to selection NS_RELEASE(endcontent); } else { if (SELECTION_DEBUG) printf("New Content is before, Appending to Beginning\n"); addRangeToSelectionTrackers(newContent, selEndContent, kInsertInAddList); // add to selection mStartSelectionPoint->SetPoint(newContent, mStartPos, PR_FALSE); } } else { if (SELECTION_DEBUG) printf("Adding full range to removed selection. (insert selection)\n"); addRangeToSelectionTrackers(selStartContent, selEndContent, kInsertInRemoveList); // removed from selection mEndSelectionPoint->SetPoint(newContent, mStartPos, PR_TRUE); mStartSelectionPoint->SetPoint(newContent, mStartPos, PR_TRUE); addRangeToSelectionTrackers(newContent, newContent, kInsertInAddList); // add to selection } } else { // Content is After End if (mouseEvent->isShift) { if (selStartContent == nsnull && selEndContent == nsnull) { // Shift Click without first clicking in the window // is interpreted the same as just clicking in a window mEndSelectionPoint->SetPoint(newContent, mStartPos, PR_TRUE); mStartSelectionPoint->SetPoint(newContent, mStartPos, PR_TRUE); addRangeToSelectionTrackers(newContent, newContent, kInsertInAddList); // add to selection } else if (mStartSelectionPoint->IsAnchor()) { if (SELECTION_DEBUG) printf("New Content is after, Append new content\n"); addRangeToSelectionTrackers(selEndContent, newContent, kInsertInAddList); // add to selection mEndSelectionPoint->SetPoint(newContent, mStartPos, PR_FALSE); } else { if (SELECTION_DEBUG) printf("New Content is after, End will now be Start\n"); addRangeToSelectionTrackers(selStartContent, selEndContent, kInsertInRemoveList); // removed from selection mStartSelectionPoint->SetPoint(selEndContent, mEndSelectionPoint->GetOffset(), PR_TRUE); mEndSelectionPoint->SetPoint(newContent, mStartPos, PR_FALSE); addRangeToSelectionTrackers(newContent, newContent, kInsertInAddList); // add to selection } } else { if (TRACKER_DEBUG) printf("Adding full range to removed selection.\n"); addRangeToSelectionTrackers(selStartContent, selEndContent, kInsertInRemoveList); // removed from selection mEndSelectionPoint->SetPoint(newContent, mStartPos, PR_TRUE); mStartSelectionPoint->SetPoint(newContent, mStartPos, PR_TRUE); addRangeToSelectionTrackers(newContent, newContent, kInsertInAddList); // add to selection } } mSelectionRange->SetStartPoint(mStartSelectionPoint); mSelectionRange->SetEndPoint(mEndSelectionPoint); //if (selStartContent) printf("selStartContent count %d\n", selStartContent->Release()); //if (selEndContent) printf("selEndContent count %d\n", selEndContent->Release()); //if (newContent) printf("newContent count %d\n", newContent->Release()); NS_IF_RELEASE(selStartContent); NS_IF_RELEASE(selEndContent); NS_IF_RELEASE(newContent); RefreshFromContentTrackers(aPresContext); if (SELECTION_DEBUG) printf("HandleEvent::mSelectionRange %s\n", mSelectionRange->ToString()); NS_IF_RELEASE(acx); // Force Update ForceDrawFrame(this); NS_RELEASE(shell); NS_RELEASE(selection); aEventStatus = nsEventStatus_eIgnore; #endif //0 return NS_OK; } NS_IMETHODIMP nsFrame::HandleDrag(nsIPresContext& aPresContext, nsGUIEvent* aEvent, nsEventStatus& aEventStatus) { if (DisplaySelection(aPresContext, PR_TRUE) == PR_FALSE) { aEventStatus = nsEventStatus_eIgnore; return NS_OK; } mDoingSelection = PR_TRUE; mDidDrag = PR_FALSE; nsIPresShell *shell = aPresContext.GetShell(); if (shell){ nsIRenderingContext * acx; if (NS_SUCCEEDED(shell->CreateRenderingContext(this, acx))){ PRInt32 startPos = 0; PRUint32 contentOffset = 0; if (NS_SUCCEEDED(GetPosition(aPresContext, acx, aEvent, this, contentOffset, startPos))){ nsISelection *selection = nsnull; if (NS_SUCCEEDED(shell->GetSelection(&selection))){ nsIFocusTracker *tracker; if (NS_SUCCEEDED(shell->QueryInterface(kIFocusTracker,(void **)&tracker))){ selection->TakeFocus(tracker, this, startPos, contentOffset, PR_TRUE); //TRUE IS THE DIFFERENCE NS_RELEASE(tracker); } NS_RELEASE(selection); } //no release } NS_RELEASE(acx); } NS_RELEASE(shell); } #if 0 //DEBUG MJUDGE if (DisplaySelection(aPresContext) == PR_FALSE) { aEventStatus = nsEventStatus_eIgnore; return NS_OK; } // Keep old start and end nsIContent * startContent = mSelectionRange->GetStartContent(); // ref counted nsIContent * endContent = mSelectionRange->GetEndContent(); // ref counted mDidDrag = PR_TRUE; nsIPresShell *ps = aPresContext.GetShell(); nsIRenderingContext *acx; ps->CreateRenderingContext(this, acx); NS_RELEASE(ps); //if (aFrame != nsnull) { //printf("nsFrame::HandleDrag\n"); // Check to see if we have changed frame if (this != mCurrentFrame) { // We are in a new Frame! if (SELECTION_DEBUG) printf("HandleDrag::Different Frame in selection!\n"); // Get Content for current Frame nsIContent * currentContent; mCurrentFrame->GetContent(currentContent); // Get Content for New Frame nsIContent * newContent; this->GetContent(newContent); // Check to see if we are still in the same Content if (currentContent == newContent) { if (SELECTION_DEBUG) printf("HandleDrag::New Frame, same content.\n"); AdjustPointsInSameContent(aPresContext, acx, aEvent); addRangeToSelectionTrackers(currentContent, currentContent, kInsertInAddList); } else if (gDoc->IsBefore(newContent, currentContent)) { if (SELECTION_DEBUG) printf("HandleDrag::New Frame, is Before.\n"); resetContentTrackers(); NewContentIsBefore(aPresContext, acx, aEvent, newContent, currentContent, this); } else { // Content is AFTER if (SELECTION_DEBUG) printf("HandleDrag::New Frame, is After.\n"); resetContentTrackers(); NewContentIsAfter(aPresContext, acx, aEvent, newContent, currentContent, this); } mCurrentFrame = this; NS_IF_RELEASE(currentContent); NS_IF_RELEASE(newContent); } else if ((nsnull != mStartSelectionPoint) && (nsnull != mEndSelectionPoint)) { if (SELECTION_DEBUG) printf("HandleDrag::Same Frame.\n"); // Same Frame as before //if (SELECTION_DEBUG) printf("\nSame Start: %s\n", mStartSelectionPoint->ToString()); //if (SELECTION_DEBUG) printf("Same End: %s\n", mEndSelectionPoint->ToString()); // [TODO] Uncomment these soon nsIContent * selStartContent = mStartSelectionPoint->GetContent(); nsIContent * selEndContent = mEndSelectionPoint->GetContent(); if (selStartContent == selEndContent) { if (SELECTION_DEBUG) printf("Start & End Frame are the same: \n"); AdjustPointsInSameContent(aPresContext, acx, aEvent); } else { if (SELECTION_DEBUG) printf("Start & End Frame are different: \n"); // Get Content for New Frame nsIContent * newContent; this->GetContent(newContent); PRInt32 newPos = -1; PRUint32 actualOffset = 0; GetPosition(aPresContext, acx, aEvent, this, actualOffset, newPos); if (newContent == selStartContent) { if (SELECTION_DEBUG) printf("New Content equals Start Content\n"); mStartSelectionPoint->SetOffset(newPos); mSelectionRange->SetStartPoint(mStartSelectionPoint); } else if (newContent == selEndContent) { if (SELECTION_DEBUG) printf("New Content equals End Content\n"); mEndSelectionPoint->SetOffset(newPos); mSelectionRange->SetEndPoint(mEndSelectionPoint); } else { if (SELECTION_DEBUG) printf("=====\n=====\n=====\n=====\n=====\n=====\n Should NOT be here.\n"); } //if (SELECTION_DEBUG) printf("*Same Start: "+mStartSelectionPoint->GetOffset()+ // " "+mStartSelectionPoint->IsAnchor()+ // " End: "+mEndSelectionPoint->GetOffset() + // " "+mEndSelectionPoint->IsAnchor()); NS_IF_RELEASE(newContent); } NS_IF_RELEASE(selStartContent); NS_IF_RELEASE(selEndContent); } //} NS_IF_RELEASE(startContent); NS_IF_RELEASE(endContent); NS_IF_RELEASE(acx); // Force Update ForceDrawFrame(this); //RefreshContentFrames(aPresContext, startContent, endContent); RefreshFromContentTrackers(aPresContext); aEventStatus = nsEventStatus_eIgnore; #endif //0 return NS_OK; } NS_IMETHODIMP nsFrame::HandleRelease(nsIPresContext& aPresContext, nsGUIEvent* aEvent, nsEventStatus& aEventStatus) { mDoingSelection = PR_FALSE; aEventStatus = nsEventStatus_eIgnore; NS_IF_RELEASE(gDoc); return NS_OK; } //-------------------------------------------------------------------------- //-- GetPosition //-------------------------------------------------------------------------- NS_IMETHODIMP nsFrame::GetPosition(nsIPresContext& aPresContext, nsIRenderingContext * aRendContext, nsGUIEvent * aEvent, nsIFrame * aNewFrame, PRUint32& aAcutalContentOffset, PRInt32& aOffset) { //PRInt32 offset; //PRInt32 width; //CalcCursorPosition(aPresContext, aEvent, aNewFrame, offset, width); //offset += aNewFrame->GetContentOffset(); //return offset; aAcutalContentOffset = 0; aOffset = -1; return NS_OK; } /******************************************************** * Adjusts the Starting and Ending TextPoint for a Range *********************************************************/ void nsFrame::AdjustPointsInNewContent(nsIPresContext& aPresContext, nsIRenderingContext * aRendContext, nsGUIEvent * aEvent, nsIFrame * aNewFrame) { #if 0 PRUint32 actualOffset = 0; // Get new Cursor Poition in the new content PRInt32 newPos; //DEBUG MJUDGE GetPosition(aPresContext, aRendContext, aEvent, aNewFrame, actualOffset, newPos); if (mStartSelectionPoint->IsAnchor()) { if (newPos == mStartSelectionPoint->GetOffset()) { mEndSelectionPoint->SetOffset(newPos); mEndSelectionPoint->SetAnchor(PR_TRUE); mSelectionRange->SetEndPoint(mEndSelectionPoint); } else if (newPos < mStartSelectionPoint->GetOffset()) { mEndSelectionPoint->SetOffset(mStartSelectionPoint->GetOffset()); mEndSelectionPoint->SetAnchor(PR_TRUE); mStartSelectionPoint->SetOffset(newPos); mStartSelectionPoint->SetAnchor(PR_FALSE); mSelectionRange->SetRange(mStartSelectionPoint, mEndSelectionPoint); } else { mEndSelectionPoint->SetOffset(newPos); mSelectionRange->SetEndPoint(mEndSelectionPoint); } } else if (mEndSelectionPoint->IsAnchor()) { int endPos = mEndSelectionPoint->GetOffset(); if (newPos == mEndSelectionPoint->GetOffset()) { mStartSelectionPoint->SetOffset(newPos); mStartSelectionPoint->SetAnchor(PR_TRUE); mSelectionRange->SetStartPoint(mStartSelectionPoint); } else if (newPos > mEndSelectionPoint->GetOffset()) { mEndSelectionPoint->SetOffset(newPos); mEndSelectionPoint->SetAnchor(PR_FALSE); mStartSelectionPoint->SetOffset(endPos); mStartSelectionPoint->SetAnchor(PR_TRUE); mSelectionRange->SetRange(mStartSelectionPoint, mEndSelectionPoint); } else { mStartSelectionPoint->SetOffset(newPos); mSelectionRange->SetStartPoint(mStartSelectionPoint); } } else { // [TODO] Should get here // throw exception if (SELECTION_DEBUG) printf("--\n--\n--\n--\n--\n--\n--\n Should be here. #102\n"); //return; } #endif //0 } /******************************************************** * Adjusts the Starting and Ending TextPoint for a Range *********************************************************/ void nsFrame::AdjustPointsInSameContent(nsIPresContext& aPresContext, nsIRenderingContext * aRendContext, nsGUIEvent * aEvent) { #if 0 PRUint32 actualOffset = 0; // Get new Cursor Poition in the same content PRInt32 newPos; GetPosition(aPresContext, aRendContext, aEvent, mCurrentFrame, actualOffset, newPos); //newPos += actualOffset; if (SELECTION_DEBUG) printf("AdjustTextPointsInSameContent newPos: %d\n", newPos); if (mStartSelectionPoint->IsAnchor()) { if (newPos == mStartSelectionPoint->GetOffset()) { mEndSelectionPoint->SetOffset(newPos); mEndSelectionPoint->SetAnchor(PR_TRUE); mSelectionRange->SetEndPoint(mEndSelectionPoint); } else if (newPos < mStartSelectionPoint->GetOffset()) { mStartSelectionPoint->SetOffset(newPos); mStartSelectionPoint->SetAnchor(PR_FALSE); mEndSelectionPoint->SetAnchor(PR_TRUE); mSelectionRange->SetRange(mStartSelectionPoint, mEndSelectionPoint); } else { mEndSelectionPoint->SetAnchor(PR_FALSE); mEndSelectionPoint->SetOffset(newPos); mSelectionRange->SetEndPoint(mEndSelectionPoint); } } else if (mEndSelectionPoint->IsAnchor()) { if (newPos == mEndSelectionPoint->GetOffset()) { mStartSelectionPoint->SetOffset(newPos); mStartSelectionPoint->SetAnchor(PR_TRUE); mSelectionRange->SetStartPoint(mStartSelectionPoint); } else if (newPos > mEndSelectionPoint->GetOffset()) { mEndSelectionPoint->SetOffset(newPos); mEndSelectionPoint->SetAnchor(PR_FALSE); mStartSelectionPoint->SetAnchor(PR_TRUE); mSelectionRange->SetRange(mStartSelectionPoint, mEndSelectionPoint); } else { mStartSelectionPoint->SetAnchor(PR_FALSE); mStartSelectionPoint->SetOffset(newPos); mSelectionRange->SetStartPoint(mStartSelectionPoint); } } else { // [TODO] Should get here // throw exception if (SELECTION_DEBUG) printf("--\n--\n--\n--\n--\n--\n--\n Should be here. #101\n"); //return; } if (SELECTION_DEBUG) printf("Start %s End %s\n", mStartSelectionPoint->ToString(), mEndSelectionPoint->ToString()); //} #endif //0 } NS_IMETHODIMP nsFrame::GetCursor(nsIPresContext& aPresContext, nsPoint& aPoint, PRInt32& aCursor) { const nsStyleColor* styleColor; GetStyleData(eStyleStruct_Color, (const nsStyleStruct*&)styleColor); aCursor = styleColor->mCursor; if ((NS_STYLE_CURSOR_AUTO == aCursor) && (nsnull != mParent)) { mParent->GetCursor(aPresContext, aPoint, aCursor); } return NS_OK; } NS_IMETHODIMP nsFrame::GetFrameForPoint(const nsPoint& aPoint, nsIFrame** aFrame) { *aFrame = this; return NS_OK; } // Resize and incremental reflow NS_IMETHODIMP nsFrame::GetFrameState(nsFrameState& aResult) { aResult = mState; return NS_OK; } NS_IMETHODIMP nsFrame::SetFrameState(nsFrameState aNewState) { mState = aNewState; return NS_OK; } // nsIHTMLReflow member functions NS_IMETHODIMP nsFrame::WillReflow(nsIPresContext& aPresContext) { NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS, ("WillReflow: oldState=%x", mState)); mState |= NS_FRAME_IN_REFLOW; return NS_OK; } NS_IMETHODIMP nsFrame::DidReflow(nsIPresContext& aPresContext, nsDidReflowStatus aStatus) { NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS, ("nsFrame::DidReflow: aStatus=%d", aStatus)); if (NS_FRAME_REFLOW_FINISHED == aStatus) { mState &= ~(NS_FRAME_IN_REFLOW | NS_FRAME_FIRST_REFLOW); // Size and position the view if requested if ((nsnull != mView) && (NS_FRAME_SYNC_FRAME_AND_VIEW & mState)) { // Position and size view relative to its parent, not relative to our // parent frame (our parent frame may not have a view). nsIView* parentWithView; nsPoint origin; GetOffsetFromView(origin, parentWithView); nsIViewManager *vm; mView->GetViewManager(vm); vm->ResizeView(mView, mRect.width, mRect.height); vm->MoveViewTo(mView, origin.x, origin.y); NS_RELEASE(vm); } } return NS_OK; } NS_IMETHODIMP nsFrame::Reflow(nsIPresContext& aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { aDesiredSize.width = 0; aDesiredSize.height = 0; aDesiredSize.ascent = 0; aDesiredSize.descent = 0; if (nsnull != aDesiredSize.maxElementSize) { aDesiredSize.maxElementSize->width = 0; aDesiredSize.maxElementSize->height = 0; } aStatus = NS_FRAME_COMPLETE; return NS_OK; } NS_IMETHODIMP nsFrame::FindTextRuns(nsLineLayout& aLineLayout) { aLineLayout.EndTextRun(); return NS_OK; } NS_IMETHODIMP nsFrame::AdjustFrameSize(nscoord aExtraSpace, nscoord& aUsedSpace) { aUsedSpace = 0; return NS_OK; } NS_IMETHODIMP nsFrame::TrimTrailingWhiteSpace(nsIPresContext& aPresContext, nsIRenderingContext& aRC, nscoord& aDeltaWidth) { aDeltaWidth = 0; return NS_OK; } NS_IMETHODIMP nsFrame::MoveInSpaceManager(nsIPresContext& aPresContext, nsISpaceManager* aSpaceManager, nscoord aDeltaX, nscoord aDeltaY) { return NS_OK; } NS_IMETHODIMP nsFrame::ContentChanged(nsIPresContext* aPresContext, nsIContent* aChild, nsISupports* aSubContent) { nsIPresShell* shell; shell = aPresContext->GetShell(); nsIReflowCommand* reflowCmd; nsresult rv = NS_NewHTMLReflowCommand(&reflowCmd, this, nsIReflowCommand::ContentChanged); if (NS_SUCCEEDED(rv)) { shell->AppendReflowCommand(reflowCmd); NS_RELEASE(reflowCmd); } NS_RELEASE(shell); return NS_OK; } NS_IMETHODIMP nsFrame::AttributeChanged(nsIPresContext* aPresContext, nsIContent* aChild, nsIAtom* aAttribute, PRInt32 aHint) { return NS_OK; } // Flow member functions NS_IMETHODIMP nsFrame::IsSplittable(nsSplittableType& aIsSplittable) const { aIsSplittable = NS_FRAME_NOT_SPLITTABLE; return NS_OK; } NS_IMETHODIMP nsFrame::CreateContinuingFrame(nsIPresContext& aPresContext, nsIFrame* aParent, nsIStyleContext* aStyleContext, nsIFrame*& aContinuingFrame) { NS_ERROR("not splittable"); aContinuingFrame = nsnull; return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsFrame::GetPrevInFlow(nsIFrame*& aPrevInFlow) const { aPrevInFlow = nsnull; return NS_OK; } NS_IMETHODIMP nsFrame::SetPrevInFlow(nsIFrame*) { NS_ERROR("not splittable"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsFrame::GetNextInFlow(nsIFrame*& aNextInFlow) const { aNextInFlow = nsnull; return NS_OK; } NS_IMETHODIMP nsFrame::SetNextInFlow(nsIFrame*) { NS_ERROR("not splittable"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsFrame::AppendToFlow(nsIFrame* aAfterFrame) { NS_ERROR("not splittable"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsFrame::PrependToFlow(nsIFrame* aBeforeFrame) { NS_ERROR("not splittable"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsFrame::RemoveFromFlow() { NS_ERROR("not splittable"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsFrame::BreakFromPrevFlow() { NS_ERROR("not splittable"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsFrame::BreakFromNextFlow() { NS_ERROR("not splittable"); return NS_ERROR_NOT_IMPLEMENTED; } // Associated view object NS_IMETHODIMP nsFrame::GetView(nsIView*& aView) const { aView = mView; return NS_OK; } NS_IMETHODIMP nsFrame::SetView(nsIView* aView) { nsresult rv; if (nsnull != aView) { mView = aView; aView->SetClientData(this); rv = NS_OK; } else rv = NS_OK; return rv; } // Find the first geometric parent that has a view NS_IMETHODIMP nsFrame::GetParentWithView(nsIFrame*& aParent) const { aParent = mParent; while (nsnull != aParent) { nsIView* parView; aParent->GetView(parView); if (nsnull != parView) { break; } aParent->GetParent(aParent); } return NS_OK; } // Returns the offset from this frame to the closest geometric parent that // has a view. Also returns the containing view or null in case of error NS_IMETHODIMP nsFrame::GetOffsetFromView(nsPoint& aOffset, nsIView*& aView) const { nsIFrame* frame = (nsIFrame*)this; aView = nsnull; aOffset.MoveTo(0, 0); do { nsPoint origin; frame->GetOrigin(origin); aOffset += origin; frame->GetParent(frame); if (nsnull != frame) { frame->GetView(aView); } } while ((nsnull != frame) && (nsnull == aView)); return NS_OK; } NS_IMETHODIMP nsFrame::GetWindow(nsIWidget*& aWindow) const { nsIFrame* frame = (nsIFrame*)this; aWindow = nsnull; while (nsnull != frame) { nsIView* view; frame->GetView(view); if (nsnull != view) { view->GetWidget(aWindow); if (nsnull != aWindow) { break; } } frame->GetParentWithView(frame); } NS_POSTCONDITION(nsnull != aWindow, "no window in frame tree"); return NS_OK; } void nsFrame::Invalidate(const nsRect& aDamageRect, PRBool aImmediate) const { nsIViewManager* viewManager = nsnull; PRUint32 flags = aImmediate ? NS_VMREFRESH_IMMEDIATE : NS_VMREFRESH_NO_SYNC; if (nsnull != mView) { mView->GetViewManager(viewManager); viewManager->UpdateView(mView, aDamageRect, flags); } else { nsRect rect(aDamageRect); nsPoint offset; nsIView* view; GetOffsetFromView(offset, view); NS_ASSERTION(nsnull != view, "no view"); rect += offset; view->GetViewManager(viewManager); viewManager->UpdateView(view, rect, flags); } NS_IF_RELEASE(viewManager); } // Style sizing methods NS_IMETHODIMP nsFrame::IsPercentageBase(PRBool& aBase) const { const nsStylePosition* position; GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)position); if (position->mPosition != NS_STYLE_POSITION_NORMAL) { aBase = PR_TRUE; } else { const nsStyleDisplay* display; GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&)display); if ((display->mDisplay == NS_STYLE_DISPLAY_BLOCK) || (display->mDisplay == NS_STYLE_DISPLAY_LIST_ITEM) || (display->mDisplay == NS_STYLE_DISPLAY_TABLE_CELL)) { aBase = PR_TRUE; } else { aBase = PR_FALSE; } } return NS_OK; } NS_IMETHODIMP nsFrame::GetAutoMarginSize(PRUint8 aSide, nscoord& aSize) const { aSize = 0; // XXX probably not right, subclass override? return NS_OK; } // Sibling pointer used to link together frames NS_IMETHODIMP nsFrame::GetNextSibling(nsIFrame*& aNextSibling) const { aNextSibling = mNextSibling; return NS_OK; } NS_IMETHODIMP nsFrame::SetNextSibling(nsIFrame* aNextSibling) { NS_ASSERTION(aNextSibling != this, "attempt to create circular frame list"); mNextSibling = aNextSibling; return NS_OK; } // Transparency query NS_IMETHODIMP nsFrame::IsTransparent(PRBool& aTransparent) const { //XXX this needs to be overridden in just about every leaf class? MMP aTransparent = PR_TRUE; return NS_OK; } NS_IMETHODIMP nsFrame::Scrolled(nsIView *aView) { return NS_OK; } PRInt32 nsFrame::ContentIndexInContainer(const nsIFrame* aFrame) { nsIContent* content; PRInt32 result = -1; aFrame->GetContent(content); if (nsnull != content) { nsIContent* parentContent; content->GetParent(parentContent); if (nsnull != parentContent) { parentContent->IndexOf(content, result); NS_RELEASE(parentContent); } NS_RELEASE(content); } return result; } // Debugging NS_IMETHODIMP nsFrame::List(FILE* out, PRInt32 aIndent) const { nsAutoString tagString; if (nsnull != mContent) { nsIAtom* tag; mContent->GetTag(tag); if (tag != nsnull) { tag->ToString(tagString); NS_RELEASE(tag); } } // Indent IndentBy(out, aIndent); // Output the tag and rect ListTag(out); if (nsnull != mView) { fprintf(out, " [view=%p]", mView); } fputs(" ", out); out << mRect; if (0 != mState) { fprintf(out, " [state=%08x]", mState); } fputs("<>\n", out); return NS_OK; } NS_IMETHODIMP nsFrame::GetFrameName(nsString& aResult) const { return MakeFrameName("Frame", aResult); } nsresult nsFrame::MakeFrameName(const char* aType, nsString& aResult) const { aResult = aType; if (nsnull != mContent) { nsIAtom* tag; mContent->GetTag(tag); if ((tag != nsnull) && (tag != nsLayoutAtoms::textTagName)) { aResult.Append("("); nsAutoString buf; tag->ToString(buf); aResult.Append(buf); NS_RELEASE(tag); aResult.Append(")"); } } char buf[40]; PR_snprintf(buf, sizeof(buf), "(%d)", ContentIndexInContainer(this)); aResult.Append(buf); return NS_OK; } void nsFrame::XMLQuote(nsString& aString) { PRInt32 i, len = aString.Length(); for (i = 0; i < len; i++) { PRUnichar ch = aString[i]; if (ch == '<') { nsAutoString tmp("<"); aString.Cut(i, 1); aString.Insert(tmp, i); len += 3; i += 3; } else if (ch == '>') { nsAutoString tmp(">"); aString.Cut(i, 1); aString.Insert(tmp, i); len += 3; i += 3; } else if (ch == '\"') { nsAutoString tmp("""); aString.Cut(i, 1); aString.Insert(tmp, i); len += 5; i += 5; } } } NS_IMETHODIMP nsFrame::DumpRegressionData(FILE* out, PRInt32 aIndent) { IndentBy(out, aIndent); fprintf(out, "\n"); aIndent++; DumpBaseRegressionData(out, aIndent); aIndent--; IndentBy(out, aIndent); fprintf(out, "\n"); return NS_OK; } void nsFrame::DumpBaseRegressionData(FILE* out, PRInt32 aIndent) { IndentBy(out, aIndent); fprintf(out, "\n", this); if (nsnull != mParent) { IndentBy(out, aIndent); fprintf(out, "\n", mParent); } if (nsnull != mNextSibling) { IndentBy(out, aIndent); fprintf(out, "\n", mNextSibling); } if (0 != mState) { IndentBy(out, aIndent); fprintf(out, "\n", mState); } if (nsnull != mView) { IndentBy(out, aIndent); fprintf(out, "\n", mView); aIndent++; // XXX add in code to dump out view state too... aIndent--; IndentBy(out, aIndent); fprintf(out, "\n"); } IndentBy(out, aIndent); fprintf(out, "\n", mRect.x, mRect.y, mRect.width, mRect.height); // Now dump all of the children on all of the child lists nsIFrame* kid; nsIAtom* list = nsnull; PRInt32 listIndex = 0; do { nsresult rv = FirstChild(list, kid); if (NS_SUCCEEDED(rv) && (nsnull != kid)) { IndentBy(out, aIndent); if (nsnull != list) { nsAutoString listName; list->ToString(listName); fprintf(out, "\n"); } else { fprintf(out, "\n"); } aIndent++; while (nsnull != kid) { kid->DumpRegressionData(out, aIndent); kid->GetNextSibling(kid); } aIndent--; IndentBy(out, aIndent); fprintf(out, "\n"); } NS_IF_RELEASE(list); GetAdditionalChildListName(listIndex++, list); } while (nsnull != list); } NS_IMETHODIMP nsFrame::VerifyTree() const { NS_ASSERTION(0 == (mState & NS_FRAME_IN_REFLOW), "frame is in reflow"); return NS_OK; } NS_IMETHODIMP nsFrame::SetSelected(PRBool aSelected, PRInt32 aBeginOffset, PRInt32 aEndOffset, PRBool aForceRedraw) { if (mSelected != aSelected || aForceRedraw) { mSelected = aSelected; ForceDrawFrame(this); } return NS_OK; } NS_IMETHODIMP nsFrame::SetSelectedContentOffsets(PRBool aSelected, PRInt32 aBeginContentOffset, PRInt32 aEndContentOffset, PRInt32 aAnchorOffset, PRInt32 aFocusOffset, PRBool aForceRedraw, nsIFocusTracker *aTracker, nsIFrame **aActualSelected) { if (!aActualSelected) return NS_ERROR_NULL_POINTER; nsIFrame *child = nsnull; nsresult result = FirstChild(nsnull, child); if (NS_FAILED(result)){ *aActualSelected = this; if (aAnchorOffset > 0) aTracker->SetFocus(nsnull,this); if (aFocusOffset > 0) aTracker->SetFocus(this,nsnull); return SetSelected(aSelected, aBeginContentOffset, aEndContentOffset, aForceRedraw); } *aActualSelected = nsnull; if (aBeginContentOffset) SetSelected(PR_FALSE, 0, 0, aForceRedraw); //if all children are not selected, then neither is this while (child && NS_SUCCEEDED(result)){ result |= child->SetSelectedContentOffsets(aSelected, aBeginContentOffset, aEndContentOffset, aAnchorOffset, aFocusOffset, aForceRedraw , aTracker, aActualSelected); if (NS_SUCCEEDED(result) && aActualSelected) return result; //done. result |= child->GetNextSibling(child); } return result; } NS_IMETHODIMP nsFrame::GetSelected(PRBool *aSelected, PRInt32 *aBeginOffset, PRInt32 *aEndOffset, PRInt32 *aBeginContentOffset) { if (!aSelected ) return NS_ERROR_NULL_POINTER; *aSelected = mSelected; return NS_OK; } NS_IMETHODIMP nsFrame::PeekOffset(nsSelectionAmount aAmount, nsDirection aDirection, nsIFrame **aResultFrame, PRInt32 *aFrameOffset, PRInt32 *aContentOffset) { //default, no matter what grab next/ previous sibling. return NS_ERROR_FAILURE; } //----------------------------------------------------------------------------------- /******************************************************** * Handles a when the cursor enters new content that is before * the content that the cursor is currently in *********************************************************/ void nsFrame::NewContentIsBefore(nsIPresContext& aPresContext, nsIRenderingContext * aRendContext, nsGUIEvent * aEvent, nsIContent * aNewContent, nsIContent * aCurrentContent, nsIFrame * aNewFrame) { #if 0 //DEBUG MJUDGE if (SELECTION_DEBUG) printf("New Frame, New content is before.\n"); // Dragging mouse to the left or backward in content // // 1) The cursor is being dragged backward in the content // and the mouse is "after" the anchor in the content // and the current piece of content is being removed from the selection // // This section cover two cases: // 2) The cursor is being dragged backward in the content // and the mouse is "before" the anchor in the content // and each new piece of content is being added to the selection if ((nsnull == mStartSelectionPoint) || (nsnull == mEndSelectionPoint)) { return; } nsIPresShell * shell = aPresContext.GetShell(); nsIDocument * doc = shell->GetDocument(); nsIContent * selStartContent = mStartSelectionPoint->GetContent(); nsIContent * selEndContent = mEndSelectionPoint->GetContent(); PRBool inRange = doc->IsInRange(selStartContent, selEndContent, aNewContent); // Check to see if the new content is in the selection if (inRange) { // Case #1 - Remove Current Content from Selection (at End) if (SELECTION_DEBUG) printf("Case #1 - (Before) New Content is in selected Range.\n"); if (aNewContent == selStartContent) { // [TODO] This is where we might have to delete from end to new content // Returns the new End Point, if Start and End are on the // same content then End Point's Cursor is set to Start's mEndSelectionPoint->SetContent(selStartContent); AdjustPointsInNewContent(aPresContext, aRendContext, aEvent, aNewFrame); } else { PRUint32 actualOffset = 0; PRInt32 newPos; GetPosition(aPresContext, aRendContext, aEvent, aNewFrame, actualOffset, newPos); mEndSelectionPoint->SetPoint(aNewContent, newPos, PR_FALSE); mSelectionRange->SetEndPoint(mEndSelectionPoint); } // The current content is being removed from Selection addRangeToSelectionTrackers(aNewContent, aCurrentContent, kInsertInRemoveList); } else { // Case #2 - Add new content to selection (At Start) if (SELECTION_DEBUG) printf("Case #2 - (Before) New Content is NOT in selected Range. Moving Start Backward.\n"); PRUint32 actualOffset = 0; PRInt32 newPos; GetPosition(aPresContext, aRendContext, aEvent, aNewFrame, actualOffset, newPos); // Create new TextPoint and move Start Point backward mStartSelectionPoint->SetPoint(aNewContent, newPos, PR_FALSE); // position is set correctly in adjustTextPoints mSelectionRange->SetStartPoint(mStartSelectionPoint); // The New Content is added to Tracker addRangeToSelectionTrackers(aNewContent, aCurrentContent, kInsertInAddList); } NS_RELEASE(selStartContent); NS_RELEASE(selEndContent); NS_RELEASE(doc); NS_RELEASE(shell); #endif //0 } /******************************************************** * Refreshes each content's frame *********************************************************/ static void RefreshAllContentFrames(nsIFrame * aFrame, nsIContent * aContent) { nsIContent* frameContent; aFrame->GetContent(frameContent); if (frameContent == aContent) { ForceDrawFrame((nsFrame *)aFrame); } NS_IF_RELEASE(frameContent); aFrame->FirstChild(nsnull, aFrame); while (aFrame) { RefreshAllContentFrames(aFrame, aContent); aFrame->GetNextSibling(aFrame); } } /******************************************************** * Refreshes each content's frame *********************************************************/ static void RefreshContentFrames(nsIPresContext& aPresContext, nsIContent * aStartContent, nsIContent * aEndContent) { //------------------------------------- // Undraw all the current selected frames // XXX Kludge for now nsIPresShell *shell = aPresContext.GetShell(); nsIFrame *rootFrame; shell->GetRootFrame(rootFrame); PRBool foundStart = PR_FALSE; for (PRInt32 i=0;iFindFrameWithContent(node)); RefreshAllContentFrames(rootFrame, node); if (aStartContent == aEndContent) { break; } } else if (foundStart) { //ForceDrawFrame((nsFrame *)shell->FindFrameWithContent(node)); RefreshAllContentFrames(rootFrame, node); } else if (aEndContent == node) { //ForceDrawFrame((nsFrame *)shell->FindFrameWithContent(node)); RefreshAllContentFrames(rootFrame, node); break; } } //NS_RELEASE(rootFrame); NS_RELEASE(shell); //------------------------------------- } /******************************************************** * Handles a when the cursor enters new content that is After * the content that the cursor is currently in *********************************************************/ void nsFrame::NewContentIsAfter(nsIPresContext& aPresContext, nsIRenderingContext * aRendContext, nsGUIEvent * aEvent, nsIContent * aNewContent, nsIContent * aCurrentContent, nsIFrame * aNewFrame) { #if 0 //DEBUG MJUDGE if (SELECTION_DEBUG) printf("New Frame, New content is after.\n"); // Dragging Mouse to the Right // // 3) The cursor is being dragged foward in the content // and the mouse is "before" the anchor in the content // and the current piece of content is being removed from the selection // // This section cover two cases: // 4) The cursor is being dragged foward in the content // and the mouse is "after" the anchor in the content // and each new piece of content is being added to the selection // // Check to see if the new content is in the selection nsIPresShell * shell = aPresContext.GetShell(); nsIDocument * doc = shell->GetDocument(); nsIContent * selStartContent = mStartSelectionPoint->GetContent(); nsIContent * selEndContent = mEndSelectionPoint->GetContent(); PRBool inRange = doc->IsInRange(selStartContent, selEndContent, aNewContent); if (inRange) { // Case #3 - Remove Content (from Start) if (SELECTION_DEBUG) printf("Case #3 - (After) New Content is in selected Range.\n"); // Remove Current Content in Tracker, but leave New Content in Selection addRangeToSelectionTrackers(mStartSelectionPoint->GetContent(), aNewContent, kInsertInRemoveList); PRUint32 actualOffset = 0; // [TODO] Always get nearest Text content PRInt32 newPos; GetPosition(aPresContext, aRendContext, aEvent, aNewFrame, actualOffset, newPos); // Check to see if the new Content is the same as the End Point's if (aNewContent == selEndContent) { if (SELECTION_DEBUG) printf("New Content matches End Point\n"); mStartSelectionPoint->SetContent(aNewContent); AdjustPointsInNewContent(aPresContext, aRendContext, aEvent, aNewFrame); } else { if (SELECTION_DEBUG) printf("New Content does NOT matches End Point\n"); mStartSelectionPoint->SetPoint(aNewContent, newPos, PR_FALSE); mSelectionRange->SetStartPoint(mStartSelectionPoint); } } else { if (SELECTION_DEBUG) printf("Case #4 - (After) Adding New Content\n"); // Case #2 - Adding Content (at End) PRUint32 actualOffset = 0; // The new content is not in the selection PRInt32 newPos; GetPosition(aPresContext, aRendContext, aEvent, aNewFrame, actualOffset, newPos); // Check to see if we need to create a new SelectionPoint and add it // or do we simply move the existing start or end point if (selStartContent == selEndContent) { if (SELECTION_DEBUG) printf("Case #4 - Start & End Content the Same\n"); // Move start or end point // Get new Cursor Poition in the new content if (mStartSelectionPoint->IsAnchor()) { if (SELECTION_DEBUG) printf("Case #4 - Start is Anchor\n"); // Since the Start is the Anchor just adjust the end // XXX Note this includes the current End point (it should be end->nextContent) addRangeToSelectionTrackers(selEndContent, aNewContent, kInsertInAddList); mEndSelectionPoint->SetPoint(aNewContent, newPos, PR_FALSE); mSelectionRange->SetEndPoint(mEndSelectionPoint); } else { if (SELECTION_DEBUG) printf("Case #4 - Start is NOT Anchor\n"); // Because End was the anchor, we need to set the Start Point to // the End's Offset and set it to be the new anchor addRangeToSelectionTrackers(selStartContent,selEndContent, kInsertInRemoveList); int endPos = mEndSelectionPoint->GetOffset(); mStartSelectionPoint->SetOffset(endPos); mStartSelectionPoint->SetAnchor(PR_TRUE); // The Start point was being moved so when it jumped to the new frame // we needed to make it the new End Point mEndSelectionPoint->SetPoint(aNewContent, newPos, PR_FALSE); mSelectionRange->SetRange(mStartSelectionPoint, mEndSelectionPoint); // The Content values have changed so go get new contents nsIContent * startContent = mStartSelectionPoint->GetContent(); nsIContent * endContent = mEndSelectionPoint->GetContent(); addRangeToSelectionTrackers(startContent, endContent, kInsertInRemoveList); NS_RELEASE(startContent); NS_RELEASE(endContent); } } else { if (SELECTION_DEBUG) printf("Case #4 - Start & End Content NOT the Same\n"); // Adjust the end point mEndSelectionPoint->SetPoint(aNewContent, newPos, PR_FALSE); mSelectionRange->SetRange(mStartSelectionPoint, mEndSelectionPoint); // Add New Content to Selection Tracker // The Content values have changed so go get new contents // NOTE: selEndContent holds the "old" end content pointer // and endContent hold the "new" content pointer nsIContent * endContent = mEndSelectionPoint->GetContent(); addRangeToSelectionTrackers(selEndContent, endContent, kInsertInAddList); NS_RELEASE(endContent); } } NS_RELEASE(selStartContent); NS_RELEASE(selEndContent); NS_RELEASE(doc); NS_RELEASE(shell); #endif //0 } /** * */ void ForceDrawFrame(nsFrame * aFrame)//, PRBool) { if (aFrame == nsnull) { return; } nsRect rect; nsIView * view; nsPoint pnt; aFrame->GetOffsetFromView(pnt, view); aFrame->GetRect(rect); rect.x = pnt.x; rect.y = pnt.y; if (view != nsnull) { nsIViewManager * viewMgr; view->GetViewManager(viewMgr); if (viewMgr != nsnull) { viewMgr->UpdateView(view, rect, 0); NS_RELEASE(viewMgr); } //viewMgr->UpdateView(view, rect, NS_VMREFRESH_DOUBLE_BUFFER | NS_VMREFRESH_IMMEDIATE); } } ///////////////////////////////////////////////// // Selection Tracker Methods ///////////////////////////////////////////////// //---------------------------- // //---------------------------- static void resetContentTrackers() { PRInt32 i; for (i=0;iGetRootFrame(rootFrame); for (i=0;iFindFrameWithContent(fTrackerContentArrayRemoveList[i])); if (SELECTION_DEBUG) printf("ForceDrawFrame (remove) content 0x%X\n", fTrackerContentArrayRemoveList[i]); } for (i=0;iFindFrameWithContent(fTrackerContentArrayAddList[i]); //ForceDrawFrame((nsFrame *)frame); RefreshAllContentFrames(rootFrame, fTrackerContentArrayAddList[i]); if (SELECTION_DEBUG) printf("ForceDrawFrame (add) content 0x%X\n", fTrackerContentArrayAddList[i]); } NS_RELEASE(shell); resetContentTrackers(); } //---------------------------- // //---------------------------- static void addRangeToSelectionTrackers(nsIContent * aStartContent, nsIContent * aEndContent, PRUint32 aType) { if (aStartContent == nsnull || aEndContent == nsnull) { return; } nsIContent ** contentList = (aType == kInsertInRemoveList?fTrackerContentArrayRemoveList:fTrackerContentArrayAddList); int inx = (aType == kInsertInRemoveList?fTrackerRemoveListMax:fTrackerAddListMax); NS_ADDREF(aStartContent); nsIContent * contentPtr = aStartContent; while (contentPtr != aEndContent) { contentList[inx++] = contentPtr; contentPtr = gDoc->GetNextContent(contentPtr); // This does an AddRef if (nsnull == contentPtr) { // XXX We didn't find the end content... if (aType == kInsertInRemoveList) { fTrackerRemoveListMax = inx; } else { // kInsertInAddList fTrackerAddListMax = inx; } return; } } contentList[inx++] = aEndContent; if (SELECTION_DEBUG) printf("Adding to %s %d\n", (aType == kInsertInRemoveList?"Remove Array":"Add Array"), inx); if (aType == kInsertInRemoveList) { fTrackerRemoveListMax = inx; } else { // kInsertInAddList fTrackerAddListMax = inx; } } #ifdef NS_DEBUG static void GetTagName(nsFrame* aFrame, nsIContent* aContent, PRIntn aResultSize, char* aResult) { char namebuf[40]; namebuf[0] = 0; if (nsnull != aContent) { nsIAtom* tag; aContent->GetTag(tag); if (nsnull != tag) { nsAutoString tmp; tag->ToString(tmp); tmp.ToCString(namebuf, sizeof(namebuf)); NS_RELEASE(tag); } } PR_snprintf(aResult, aResultSize, "%s@%p", namebuf, aFrame); } void nsFrame::Trace(const char* aMethod, PRBool aEnter) { if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) { char tagbuf[40]; GetTagName(this, mContent, sizeof(tagbuf), tagbuf); PR_LogPrint("%s: %s %s", tagbuf, aEnter ? "enter" : "exit", aMethod); } } void nsFrame::Trace(const char* aMethod, PRBool aEnter, nsReflowStatus aStatus) { if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) { char tagbuf[40]; GetTagName(this, mContent, sizeof(tagbuf), tagbuf); PR_LogPrint("%s: %s %s, status=%scomplete%s", tagbuf, aEnter ? "enter" : "exit", aMethod, NS_FRAME_IS_NOT_COMPLETE(aStatus) ? "not" : "", (NS_FRAME_REFLOW_NEXTINFLOW & aStatus) ? "+reflow" : ""); } } void nsFrame::TraceMsg(const char* aFormatString, ...) { if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) { // Format arguments into a buffer char argbuf[200]; va_list ap; va_start(ap, aFormatString); PR_vsnprintf(argbuf, sizeof(argbuf), aFormatString, ap); va_end(ap); char tagbuf[40]; GetTagName(this, mContent, sizeof(tagbuf), tagbuf); PR_LogPrint("%s: %s", tagbuf, argbuf); } } #endif