Mozilla/mozilla/layout/xul/base/src/nsTreeRowFrame.cpp

421 lines
13 KiB
C++

/* -*- 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.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 Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Original Author: David W. Hyatt (hyatt@netscape.com)
*
* Contributor(s):
*/
#include "nsCOMPtr.h"
#include "nsTreeFrame.h"
#include "nsTreeRowFrame.h"
#include "nsIStyleContext.h"
#include "nsIContent.h"
#include "nsCSSRendering.h"
#include "nsTreeCellFrame.h"
#include "nsCellMap.h"
#include "nsIDOMXULTreeElement.h"
#include "nsINameSpaceManager.h"
#include "nsTreeRowGroupFrame.h"
#include "nsXULAtoms.h"
#include "nsHTMLAtoms.h"
#include "nsTableColFrame.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMXULTreeElement.h"
#include "nsTreeTwistyListener.h"
#include "nsIViewManager.h"
#include "nsIView.h"
#include "nsIPresContext.h"
//
// NS_NewTreeFrame
//
// Creates a new tree frame
//
nsresult
NS_NewTreeRowFrame (nsIPresShell* aPresShell, nsIFrame** aNewFrame)
{
NS_PRECONDITION(aNewFrame, "null OUT ptr");
if (nsnull == aNewFrame) {
return NS_ERROR_NULL_POINTER;
}
nsTreeRowFrame* it = new (aPresShell) nsTreeRowFrame;
if (!it)
return NS_ERROR_OUT_OF_MEMORY;
*aNewFrame = it;
return NS_OK;
} // NS_NewTreeRowFrame
// Constructor
nsTreeRowFrame::nsTreeRowFrame()
:nsTableRowFrame(), mIsHeader(PR_FALSE), mGeneration(0), mDraggingHeader(PR_FALSE),
mHitFrame(nsnull), mFlexingCol(nsnull), mHeaderPosition(0)
{ }
// Destructor
nsTreeRowFrame::~nsTreeRowFrame()
{
}
NS_METHOD nsTreeRowFrame::IR_TargetIsChild(nsIPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
RowReflowState& aReflowState,
nsReflowStatus& aStatus,
nsIFrame * aNextFrame)
{
nsRect rect;
GetRect(rect);
nsresult rv = nsTableRowFrame::IR_TargetIsChild(aPresContext, aDesiredSize, aReflowState, aStatus, aNextFrame);
if (rv == NS_OK) {
// Find out if our height changed. With the tree widget, changing the height of a row is a
// big deal, since it may force us to dynamically isntantiate newly exposed frames.
if (rect.height != aDesiredSize.height) {
// Retrieve the table frame and invalidate the cell map.
nsTableFrame* tableFrame = nsnull;
nsTableFrame::GetTableFrame(this, tableFrame);
tableFrame->InvalidateColumnWidths();
}
}
return rv;
}
NS_IMETHODIMP
nsTreeRowFrame::Init(nsIPresContext* aPresContext,
nsIContent* aContent,
nsIFrame* aParent,
nsIStyleContext* aContext,
nsIFrame* aPrevInFlow)
{
nsresult rv = nsTableRowFrame::Init(aPresContext, aContent, aParent, aContext,
aPrevInFlow);
// Determine if we're a column header or not.
// Get row group frame
if (aParent != nsnull)
{
// Get the display type of the row group frame and see if it's a header or body
nsCOMPtr<nsIStyleContext> parentContext;
aParent->GetStyleContext(getter_AddRefs(parentContext));
if (parentContext)
{
const nsStyleDisplay* display = (const nsStyleDisplay*)
parentContext->GetStyleData(eStyleStruct_Display);
if (display->mDisplay == NS_STYLE_DISPLAY_TABLE_HEADER_GROUP)
{
mIsHeader = PR_TRUE;
// headers get their own views, so that they can capture events
CreateViewForFrame(aPresContext,this,aContext,PR_TRUE);
nsIView* view;
GetView(aPresContext, &view);
view->SetContentTransparency(PR_TRUE);
}
else
{
mIsHeader = PR_FALSE;
// Determine the row's generation.
nsTableFrame* tableFrame;
nsTableFrame::GetTableFrame(aParent, tableFrame);
nsTreeFrame* treeFrame = (nsTreeFrame*)tableFrame;
mGeneration = treeFrame->GetCurrentGeneration();
}
}
}
return rv;
}
NS_IMETHODIMP
nsTreeRowFrame::HeaderDrag(nsIPresContext* aPresContext, PRBool aGrabMouseEvents)
{
// get its view
nsIView* view = nsnull;
GetView(aPresContext, &view);
nsCOMPtr<nsIViewManager> viewMan;
PRBool result;
if (view) {
view->GetViewManager(*getter_AddRefs(viewMan));
if (viewMan) {
if (aGrabMouseEvents) {
viewMan->GrabMouseEvents(view,result);
mDraggingHeader = PR_TRUE;
} else {
viewMan->GrabMouseEvents(nsnull,result);
mDraggingHeader = PR_FALSE;
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsTreeRowFrame::Reflow(nsIPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
/*
if (aReflowState.reason != eReflowReason_Incremental) {
// Determine the row's generation.
nsTableFrame* tableFrame;
nsTableFrame::GetTableFrame(this, tableFrame);
nsTreeFrame* treeFrame = (nsTreeFrame*)tableFrame;
if (treeFrame->UseGeneration()) {
PRInt32 currGeneration = treeFrame->GetCurrentGeneration();
if (currGeneration > mGeneration) {
nsRect rect;
GetRect(rect);
aDesiredSize.width = rect.width;
aDesiredSize.height = rect.height;
aStatus = NS_FRAME_COMPLETE;
return NS_OK;
}
}
}
*/
/* static int i = 0;
i++;
printf("Full row reflow! Number %d\n", i);
*/
// Debugging info.
NS_ASSERTION(aReflowState.mComputedWidth != NS_UNCONSTRAINEDSIZE,
"Reflowing tree row frame with unconstrained width!!!!");
//printf("Tree Row Width: %d, Tree Row Height: %d\n", aReflowState.mComputedWidth, aReflowState.mComputedHeight);
if (mState & NS_FRAME_FIRST_REFLOW) {
// Newly inserted frame
((nsHTMLReflowState&)aReflowState).reason = eReflowReason_Initial;
}
return nsTableRowFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
}
nsresult
nsTreeRowFrame::HandleMouseUpEvent(nsIPresContext* aPresContext,
nsGUIEvent* aEvent,
nsEventStatus* aEventStatus)
{
if (DraggingHeader()) {
HeaderDrag(aPresContext, PR_FALSE);
}
return NS_OK;
}
NS_IMETHODIMP
nsTreeRowFrame::HandleEvent(nsIPresContext* aPresContext,
nsGUIEvent* aEvent,
nsEventStatus* aEventStatus)
{
NS_ENSURE_ARG_POINTER(aEventStatus);
*aEventStatus = nsEventStatus_eConsumeDoDefault;
if (aEvent->message == NS_MOUSE_LEFT_BUTTON_UP)
HandleMouseUpEvent(aPresContext, aEvent, aEventStatus);
else if (aEvent->message == NS_MOUSE_MOVE && mDraggingHeader && mHitFrame)
HandleHeaderDragEvent(aPresContext, aEvent, aEventStatus);
return NS_OK;
}
NS_IMETHODIMP
nsTreeRowFrame::GetFrameForPoint(nsIPresContext* aPresContext,
const nsPoint& aPoint, // Overridden to capture events
nsFramePaintLayer aWhichLayer,
nsIFrame** aFrame)
{
nsresult rv = nsTableRowFrame::GetFrameForPoint(aPresContext, aPoint, aWhichLayer, aFrame);
if (mDraggingHeader) {
mHitFrame = *aFrame;
*aFrame = this;
nsRect rect;
GetRect(rect);
if (rect.x > aPoint.x || (rect.x+rect.width < aPoint.x)) {
mHitFrame = nsnull;
}
}
return rv;
}
NS_IMETHODIMP
nsTreeRowFrame::GetCursor(nsIPresContext* aPresContext,
nsPoint& aPoint,
PRInt32& aCursor)
{
if (mDraggingHeader) {
nsRect rect;
GetRect(rect);
if (rect.x > aPoint.x || (rect.x+rect.width < aPoint.x)) {
aCursor = NS_STYLE_CURSOR_DEFAULT;
}
else {
aCursor = NS_STYLE_CURSOR_W_RESIZE;
}
}
else aCursor = NS_STYLE_CURSOR_DEFAULT;
return NS_OK;
}
nsresult
nsTreeRowFrame::HandleHeaderDragEvent(nsIPresContext* aPresContext,
nsGUIEvent* aEvent,
nsEventStatus* aEventStatus)
{
// Grab our tree frame.
nsTableFrame* tableFrame = nsnull;
nsTableFrame::GetTableFrame(this, tableFrame);
nsTreeFrame* treeFrame = (nsTreeFrame*)tableFrame;
// Until we finish all of our batched operations, suppress all reflow.
// XXX How to suppress?
PRInt32 columnCount = treeFrame->GetColCount();
PRInt32* colWidths = new PRInt32[columnCount];
nsCRT::memset(colWidths, 0, columnCount*sizeof(PRInt32));
// Retrieve our column widths.
PRInt32 i;
for (i = 0; i < columnCount; i++) {
nsTableColFrame* result = treeFrame->GetColFrame(i);
nsCOMPtr<nsIContent> colContent;
result->GetContent(getter_AddRefs(colContent));
nsCOMPtr<nsIAtom> fixedAtom = dont_AddRef(NS_NewAtom("fixed"));
if (colContent) {
nsAutoString fixedValue;
colContent->GetAttribute(kNameSpaceID_None, fixedAtom, fixedValue);
if (fixedValue != "true") {
// We are a proportional column and should be annotated with our current
// width.
PRInt32 colWidth = treeFrame->GetColumnWidth(i);
colWidths[i] = colWidth;
}
}
}
// Annotate with the current proportions
for (i = 0; i < columnCount; i++) {
if (colWidths[i] > 0) {
nsTableColFrame* result = treeFrame->GetColFrame(i);
nsCOMPtr<nsIContent> colContent;
result->GetContent(getter_AddRefs(colContent));
if (colContent) {
PRInt32 colWidth = colWidths[i];
char ch[100];
sprintf(ch,"%d*", colWidth);
nsAutoString propColWidth(ch);
colContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::width, propColWidth,
PR_TRUE);
}
}
}
// Figure out how much we shifted the mouse.
char ch[100];
nsPoint point = ((nsMouseEvent*)aEvent)->point;
PRInt32 delta = mHeaderPosition - point.x;
mHeaderPosition = point.x;
// The proportional columns to the right will gain or lose space
// according to the percentages they currently consume.
nscoord propTotal = 0;
// Find our flexing col and note its index and width.
PRInt32 colX;
PRInt32 flexWidth = 0;
PRInt32 flexIndex = 0;
for (colX = 0; colX < columnCount; colX++) {
// Get column information
nsTableColFrame* colFrame = tableFrame->GetColFrame(colX);
if (colFrame == mFlexingCol) {
flexWidth = colWidths[colX];
flexIndex = colX;
break;
}
}
for (colX = flexIndex+1; colX < columnCount; colX++) {
// Retrieve the current widths for these columns and compute
// the total amount of space they occupy.
propTotal += colWidths[colX];
}
// Iterate over the columns to the right of the flexing column,
// and give them a percentage of the delta based off their proportions.
nsCOMPtr<nsIContent> colContent;
nsTableColFrame* colFrame;
PRInt32 colWidth = 0;
PRInt32 remaining = delta;
for (colX = flexIndex+1; colX < columnCount; colX++) {
if (colWidths[colX] > 0) {
colFrame = tableFrame->GetColFrame(colX);
float percentage = ((float)colWidths[colX])/((float)propTotal);
PRInt32 mod = (PRInt32)(percentage * (float)delta);
colWidth = colWidths[colX] + mod;
sprintf(ch,"%d*", colWidth);
nsAutoString propColWidth(ch);
colFrame->GetContent(getter_AddRefs(colContent));
if (colContent) {
colContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::width, propColWidth,
PR_TRUE);
}
remaining -= mod;
}
}
// Fix the spillover. We'll probably be off by a little.
if (remaining != 0 && colContent) {
colWidth += remaining;
sprintf(ch,"%d*", colWidth);
nsAutoString propColWidth(ch);
colContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::width, propColWidth,
PR_TRUE);
}
// Delete the colWidths array.
delete []colWidths;
// Modify the flexing column by the delta.
nsCOMPtr<nsIContent> flexContent;
mFlexingCol->GetContent(getter_AddRefs(flexContent));
if (flexContent) {
treeFrame->SetUseGeneration(PR_FALSE); // Cached rows have to reflow.
colWidth = flexWidth - delta;
sprintf(ch,"%d*", colWidth);
nsAutoString propColWidth(ch);
flexContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::width, propColWidth,
PR_TRUE); // NOW we send the notification that causes the reflow.
}
return NS_OK;
}