mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-06 03:09:18 +02:00
120299 - Drop feedback is narrow first time 121187 - drag of message summary line (even without drop) garbles bounding box r=sfraser, sr=hyatt
3298 lines
108 KiB
C++
3298 lines
108 KiB
C++
/* -*- 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)
|
|
* Ben Goodger <ben@netscape.com>
|
|
* Joe Hewitt <hewitt@netscape.com>
|
|
* Jan Varga <varga@utcru.sk>
|
|
* Dean Tessman <dean_tessman@hotmail.com>
|
|
* Brian Ryner <bryner@netscape.com>
|
|
* Blake Ross <blaker@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 "nsISupportsArray.h"
|
|
#include "nsIPresContext.h"
|
|
#include "nsINameSpaceManager.h"
|
|
#include "nsIScrollbarFrame.h"
|
|
|
|
#include "nsOutlinerBodyFrame.h"
|
|
#include "nsOutlinerSelection.h"
|
|
|
|
#include "nsXULAtoms.h"
|
|
#include "nsHTMLAtoms.h"
|
|
#include "nsCSSAtoms.h"
|
|
|
|
#include "nsIContent.h"
|
|
#include "nsIStyleContext.h"
|
|
#include "nsIBoxObject.h"
|
|
#include "nsGUIEvent.h"
|
|
#include "nsIDOMMouseEvent.h"
|
|
#include "nsIDOMElement.h"
|
|
#include "nsIDOMNodeList.h"
|
|
#include "nsIDOMNSDocument.h"
|
|
#include "nsIDOMXULElement.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsIContent.h"
|
|
#include "nsICSSStyleRule.h"
|
|
#include "nsCSSRendering.h"
|
|
#include "nsIFontMetrics.h"
|
|
#include "nsIDeviceContext.h"
|
|
#include "nsIXULTemplateBuilder.h"
|
|
#include "nsXPIDLString.h"
|
|
#include "nsHTMLContainerFrame.h"
|
|
#include "nsIView.h"
|
|
#include "nsWidgetsCID.h"
|
|
#include "nsBoxFrame.h"
|
|
#include "nsBoxObject.h"
|
|
#include "nsIURL.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsBoxLayoutState.h"
|
|
#include "nsIDragService.h"
|
|
#include "nsOutlinerContentView.h"
|
|
#include "nsOutlinerUtils.h"
|
|
#include "nsChildIterator.h"
|
|
#include "nsIScrollableView.h"
|
|
#include "nsITheme.h"
|
|
|
|
#ifdef USE_IMG2
|
|
#include "imgIRequest.h"
|
|
#include "imgIContainer.h"
|
|
#include "imgIContainerObserver.h"
|
|
#include "imgILoader.h"
|
|
#endif
|
|
|
|
#define ELLIPSIS "..."
|
|
|
|
// The style context cache impl
|
|
nsresult
|
|
nsOutlinerStyleCache::GetStyleContext(nsICSSPseudoComparator* aComparator,
|
|
nsIPresContext* aPresContext, nsIContent* aContent,
|
|
nsIStyleContext* aContext, nsIAtom* aPseudoElement,
|
|
nsISupportsArray* aInputWord,
|
|
nsIStyleContext** aResult)
|
|
{
|
|
*aResult = nsnull;
|
|
|
|
PRUint32 count;
|
|
aInputWord->Count(&count);
|
|
nsDFAState startState(0);
|
|
nsDFAState* currState = &startState;
|
|
|
|
// Go ahead and init the transition table.
|
|
if (!mTransitionTable) {
|
|
// Automatic miss. Build the table
|
|
mTransitionTable =
|
|
new nsObjectHashtable(nsnull, nsnull, DeleteDFAState, nsnull);
|
|
}
|
|
|
|
// The first transition is always made off the supplied pseudo-element.
|
|
nsTransitionKey key(currState->GetStateID(), aPseudoElement);
|
|
currState = NS_STATIC_CAST(nsDFAState*, mTransitionTable->Get(&key));
|
|
|
|
if (!currState) {
|
|
// We had a miss. Make a new state and add it to our hash.
|
|
currState = new nsDFAState(mNextState);
|
|
mNextState++;
|
|
mTransitionTable->Put(&key, currState);
|
|
}
|
|
|
|
for (PRUint32 i = 0; i < count; i++)
|
|
{
|
|
nsCOMPtr<nsIAtom> pseudo = getter_AddRefs(NS_STATIC_CAST(nsIAtom*, aInputWord->ElementAt(i)));
|
|
nsTransitionKey key(currState->GetStateID(), pseudo);
|
|
currState = NS_STATIC_CAST(nsDFAState*, mTransitionTable->Get(&key));
|
|
|
|
if (!currState) {
|
|
// We had a miss. Make a new state and add it to our hash.
|
|
currState = new nsDFAState(mNextState);
|
|
mNextState++;
|
|
mTransitionTable->Put(&key, currState);
|
|
}
|
|
}
|
|
|
|
// We're in a final state.
|
|
// Look up our style context for this state.
|
|
if (mCache)
|
|
*aResult = NS_STATIC_CAST(nsIStyleContext*, mCache->Get(currState)); // Addref occurs on *aResult.
|
|
if (!*aResult) {
|
|
// We missed the cache. Resolve this pseudo-style.
|
|
aPresContext->ResolvePseudoStyleWithComparator(aContent, aPseudoElement,
|
|
aContext, PR_FALSE,
|
|
aComparator,
|
|
aResult); // Addref occurs on *aResult.
|
|
// Put it in our table.
|
|
if (!mCache)
|
|
mCache = new nsSupportsHashtable;
|
|
mCache->Put(currState, *aResult);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
/* static */ PRBool PR_CALLBACK
|
|
nsOutlinerStyleCache::DeleteDFAState(nsHashKey *aKey,
|
|
void *aData,
|
|
void *closure)
|
|
{
|
|
nsDFAState* entry = NS_STATIC_CAST(nsDFAState*, aData);
|
|
delete entry;
|
|
return PR_TRUE;
|
|
}
|
|
|
|
// Column class that caches all the info about our column.
|
|
nsOutlinerColumn::nsOutlinerColumn(nsIContent* aColElement, nsIFrame* aFrame)
|
|
:mNext(nsnull)
|
|
{
|
|
mColFrame = aFrame;
|
|
mColElement = aColElement;
|
|
|
|
// Fetch the ID.
|
|
mColElement->GetAttr(kNameSpaceID_None, nsHTMLAtoms::id, mID);
|
|
|
|
// If we have an ID, cache the ID as an atom.
|
|
if (!mID.IsEmpty()) {
|
|
mIDAtom = getter_AddRefs(NS_NewAtom(mID));
|
|
}
|
|
|
|
nsCOMPtr<nsIStyleContext> styleContext;
|
|
aFrame->GetStyleContext(getter_AddRefs(styleContext));
|
|
|
|
const nsStyleVisibility* vis =
|
|
(const nsStyleVisibility*)styleContext->GetStyleData(eStyleStruct_Visibility);
|
|
|
|
// Fetch the crop style.
|
|
mCropStyle = 0;
|
|
nsAutoString crop;
|
|
mColElement->GetAttr(kNameSpaceID_None, nsXULAtoms::crop, crop);
|
|
if (crop.EqualsIgnoreCase("center"))
|
|
mCropStyle = 1;
|
|
else if (crop.EqualsIgnoreCase("left") || crop.EqualsIgnoreCase("start"))
|
|
mCropStyle = 2;
|
|
|
|
if (mCropStyle == 0 || mCropStyle == 2) { // Left or Right
|
|
if (vis->mDirection == NS_STYLE_DIRECTION_RTL)
|
|
mCropStyle = 2 - mCropStyle; // Right becomes left, left becomes right.
|
|
}
|
|
|
|
// Cache our text alignment policy.
|
|
const nsStyleText* textStyle =
|
|
(const nsStyleText*)styleContext->GetStyleData(eStyleStruct_Text);
|
|
|
|
mTextAlignment = textStyle->mTextAlign;
|
|
if (mTextAlignment == 0 || mTextAlignment == 2) { // Left or Right
|
|
if (vis->mDirection == NS_STYLE_DIRECTION_RTL)
|
|
mTextAlignment = 2 - mTextAlignment; // Right becomes left, left becomes right.
|
|
}
|
|
|
|
// Figure out if we're the primary column (that has to have indentation
|
|
// and twisties drawn.
|
|
mIsPrimaryCol = PR_FALSE;
|
|
nsAutoString primary;
|
|
mColElement->GetAttr(kNameSpaceID_None, nsXULAtoms::primary, primary);
|
|
if (primary.EqualsIgnoreCase("true"))
|
|
mIsPrimaryCol = PR_TRUE;
|
|
|
|
// Figure out if we're a cycling column (one that doesn't cause a selection
|
|
// to happen).
|
|
mIsCyclerCol = PR_FALSE;
|
|
nsAutoString cycler;
|
|
mColElement->GetAttr(kNameSpaceID_None, nsXULAtoms::cycler, cycler);
|
|
if (cycler.EqualsIgnoreCase("true"))
|
|
mIsCyclerCol = PR_TRUE;
|
|
|
|
// Cache our index.
|
|
mColIndex = -1;
|
|
nsCOMPtr<nsIContent> parent;
|
|
mColElement->GetParent(*getter_AddRefs(parent));
|
|
PRInt32 count;
|
|
parent->ChildCount(count);
|
|
PRInt32 j = 0;
|
|
for (PRInt32 i = 0; i < count; i++) {
|
|
nsCOMPtr<nsIContent> child;
|
|
parent->ChildAt(i, *getter_AddRefs(child));
|
|
nsCOMPtr<nsIAtom> tag;
|
|
child->GetTag(*getter_AddRefs(tag));
|
|
if (tag == nsXULAtoms::outlinercol) {
|
|
if (child == mColElement) {
|
|
mColIndex = j;
|
|
break;
|
|
}
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
|
|
inline nscoord nsOutlinerColumn::GetWidth()
|
|
{
|
|
if (mColFrame) {
|
|
nsRect rect;
|
|
mColFrame->GetRect(rect);
|
|
return rect.width;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// NS_NewOutlinerFrame
|
|
//
|
|
// Creates a new outliner frame
|
|
//
|
|
nsresult
|
|
NS_NewOutlinerBodyFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
|
|
{
|
|
NS_PRECONDITION(aNewFrame, "null OUT ptr");
|
|
if (nsnull == aNewFrame) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
nsOutlinerBodyFrame* it = new (aPresShell) nsOutlinerBodyFrame(aPresShell);
|
|
if (!it)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
*aNewFrame = it;
|
|
return NS_OK;
|
|
|
|
} // NS_NewOutlinerFrame
|
|
|
|
|
|
//
|
|
// QueryInterface
|
|
//
|
|
NS_INTERFACE_MAP_BEGIN(nsOutlinerBodyFrame)
|
|
NS_INTERFACE_MAP_ENTRY(nsIOutlinerBoxObject)
|
|
NS_INTERFACE_MAP_ENTRY(nsICSSPseudoComparator)
|
|
NS_INTERFACE_MAP_ENTRY(nsIScrollbarMediator)
|
|
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
|
|
NS_INTERFACE_MAP_END_INHERITING(nsLeafBoxFrame)
|
|
|
|
|
|
|
|
// Constructor
|
|
nsOutlinerBodyFrame::nsOutlinerBodyFrame(nsIPresShell* aPresShell)
|
|
:nsLeafBoxFrame(aPresShell), mPresContext(nsnull), mOutlinerBoxObject(nsnull), mImageCache(nsnull),
|
|
mColumns(nsnull), mScrollbar(nsnull), mTopRowIndex(0), mRowHeight(0), mIndentation(0), mStringWidth(-1),
|
|
mFocused(PR_FALSE), mColumnsDirty(PR_TRUE), mDropAllowed(PR_FALSE), mHasFixedRowCount(PR_FALSE),
|
|
mVerticalOverflow(PR_FALSE), mImageGuard(PR_FALSE), mDropRow(-1), mDropOrient(-1), mOpenTimer(nsnull)
|
|
{
|
|
NS_NewISupportsArray(getter_AddRefs(mScratchArray));
|
|
}
|
|
|
|
// Destructor
|
|
nsOutlinerBodyFrame::~nsOutlinerBodyFrame()
|
|
{
|
|
delete mImageCache;
|
|
}
|
|
|
|
NS_IMETHODIMP_(nsrefcnt)
|
|
nsOutlinerBodyFrame::AddRef(void)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP_(nsrefcnt)
|
|
nsOutlinerBodyFrame::Release(void)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
static nsIFrame* InitScrollbarFrame(nsIPresContext* aPresContext, nsIFrame* aCurrFrame, nsIScrollbarMediator* aSM)
|
|
{
|
|
// Check ourselves
|
|
nsCOMPtr<nsIScrollbarFrame> sf(do_QueryInterface(aCurrFrame));
|
|
if (sf) {
|
|
sf->SetScrollbarMediator(aSM);
|
|
return aCurrFrame;
|
|
}
|
|
|
|
nsIFrame* child;
|
|
aCurrFrame->FirstChild(aPresContext, nsnull, &child);
|
|
while (child) {
|
|
nsIFrame* result = InitScrollbarFrame(aPresContext, child, aSM);
|
|
if (result)
|
|
return result;
|
|
child->GetNextSibling(&child);
|
|
}
|
|
|
|
return nsnull;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame::Init(nsIPresContext* aPresContext, nsIContent* aContent,
|
|
nsIFrame* aParent, nsIStyleContext* aContext, nsIFrame* aPrevInFlow)
|
|
{
|
|
mPresContext = aPresContext;
|
|
nsresult rv = nsLeafBoxFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow);
|
|
nsBoxFrame::CreateViewForFrame(aPresContext, this, aContext, PR_TRUE);
|
|
nsIView* ourView;
|
|
nsLeafBoxFrame::GetView(aPresContext, &ourView);
|
|
|
|
static NS_DEFINE_CID(kWidgetCID, NS_CHILD_CID);
|
|
|
|
ourView->CreateWidget(kWidgetCID);
|
|
ourView->GetWidget(*getter_AddRefs(mOutlinerWidget));
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame::GetPrefSize(nsBoxLayoutState& aBoxLayoutState, nsSize& aSize)
|
|
{
|
|
if (!mView) {
|
|
mRowHeight = GetRowHeight();
|
|
EnsureBoxObject();
|
|
nsCOMPtr<nsIBoxObject> box = do_QueryInterface(mOutlinerBoxObject);
|
|
if (box) {
|
|
nsCOMPtr<nsISupports> suppView;
|
|
box->GetPropertyAsSupports(NS_LITERAL_STRING("view").get(), getter_AddRefs(suppView));
|
|
nsCOMPtr<nsIOutlinerView> outlinerView(do_QueryInterface(suppView));
|
|
|
|
if (outlinerView) {
|
|
nsXPIDLString rowStr;
|
|
box->GetProperty(NS_LITERAL_STRING("topRow").get(), getter_Copies(rowStr));
|
|
nsAutoString rowStr2(rowStr);
|
|
PRInt32 error;
|
|
PRInt32 rowIndex = rowStr2.ToInteger(&error);
|
|
|
|
// Set our view.
|
|
SetView(outlinerView);
|
|
|
|
// Scroll to the given row.
|
|
// XXX is this optimal if we haven't laid out yet?
|
|
ScrollToRow(rowIndex);
|
|
|
|
// Clear out the property info for the top row, but we always keep the
|
|
// view current.
|
|
box->RemoveProperty(NS_LITERAL_STRING("topRow").get());
|
|
}
|
|
}
|
|
|
|
if (!mView) {
|
|
// If we don't have a box object yet, or no view was set on it,
|
|
// look for a XULOutlinerBuilder or create a content view.
|
|
|
|
nsCOMPtr<nsIContent> parent;
|
|
mContent->GetParent(*getter_AddRefs(parent));
|
|
nsCOMPtr<nsIDOMXULElement> xulele = do_QueryInterface(parent);
|
|
if (xulele) {
|
|
nsCOMPtr<nsIOutlinerView> view;
|
|
|
|
// See if there is a XUL outliner builder associated with
|
|
// the parent element.
|
|
nsCOMPtr<nsIXULTemplateBuilder> builder;
|
|
xulele->GetBuilder(getter_AddRefs(builder));
|
|
if (builder)
|
|
view = do_QueryInterface(builder);
|
|
|
|
if (!view) {
|
|
// No outliner builder, create an outliner content view.
|
|
nsCOMPtr<nsIOutlinerContentView> contentView;
|
|
NS_NewOutlinerContentView(getter_AddRefs(contentView));
|
|
if (contentView)
|
|
view = do_QueryInterface(contentView);
|
|
}
|
|
|
|
// Hook up the view.
|
|
if (view)
|
|
SetView(view);
|
|
}
|
|
}
|
|
}
|
|
|
|
nsCOMPtr<nsIContent> baseElement;
|
|
GetBaseElement(getter_AddRefs(baseElement));
|
|
nsCOMPtr<nsIAtom> tag;
|
|
baseElement->GetTag(*getter_AddRefs(tag));
|
|
|
|
PRInt32 desiredRows;
|
|
if (tag == nsHTMLAtoms::select) {
|
|
aSize.width = CalcMaxRowWidth(aBoxLayoutState);
|
|
nsAutoString size;
|
|
baseElement->GetAttr(kNameSpaceID_None, nsHTMLAtoms::size, size);
|
|
if (!size.IsEmpty()) {
|
|
PRInt32 err;
|
|
desiredRows = size.ToInteger(&err);
|
|
mHasFixedRowCount = PR_TRUE;
|
|
} else
|
|
desiredRows = 1;
|
|
} else {
|
|
// outliner
|
|
aSize.width = 0;
|
|
nsAutoString rows;
|
|
baseElement->GetAttr(kNameSpaceID_None, nsXULAtoms::rows, rows);
|
|
if (!rows.IsEmpty()) {
|
|
PRInt32 err;
|
|
desiredRows = rows.ToInteger(&err);
|
|
mHasFixedRowCount = PR_TRUE;
|
|
} else
|
|
desiredRows = 0;
|
|
}
|
|
|
|
aSize.height = GetRowHeight() * desiredRows;
|
|
|
|
AddBorderAndPadding(aSize);
|
|
AddInset(aSize);
|
|
nsIBox::AddCSSPrefSize(aBoxLayoutState, this, aSize);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nscoord
|
|
nsOutlinerBodyFrame::CalcMaxRowWidth(nsBoxLayoutState& aState)
|
|
{
|
|
if (mStringWidth != -1)
|
|
return mStringWidth;
|
|
|
|
if (!mView)
|
|
return 0;
|
|
|
|
nsCOMPtr<nsIStyleContext> rowContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinerrow, getter_AddRefs(rowContext));
|
|
nsMargin rowMargin(0,0,0,0);
|
|
nsStyleBorderPadding bPad;
|
|
rowContext->GetBorderPaddingFor(bPad);
|
|
bPad.GetBorderPadding(rowMargin);
|
|
|
|
PRInt32 numRows;
|
|
mView->GetRowCount(&numRows);
|
|
|
|
nscoord rowWidth;
|
|
nsOutlinerColumn* col;
|
|
EnsureColumns();
|
|
|
|
for (PRInt32 row = 0; row < numRows; ++row) {
|
|
rowWidth = 0;
|
|
col = mColumns;
|
|
|
|
while (col) {
|
|
nscoord desiredWidth, currentWidth;
|
|
GetCellWidth(row, col->GetID(), desiredWidth, currentWidth);
|
|
rowWidth += desiredWidth;
|
|
col = col->GetNext();
|
|
}
|
|
|
|
if (rowWidth > mStringWidth)
|
|
mStringWidth = rowWidth;
|
|
}
|
|
|
|
mStringWidth += rowMargin.left + rowMargin.right;
|
|
return mStringWidth;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame::Destroy(nsIPresContext* aPresContext)
|
|
{
|
|
// Delete our column structures.
|
|
delete mColumns;
|
|
mColumns = nsnull;
|
|
|
|
// Save off our info into the box object.
|
|
if (mOutlinerBoxObject) {
|
|
nsCOMPtr<nsIBoxObject> box(do_QueryInterface(mOutlinerBoxObject));
|
|
if (mTopRowIndex > 0) {
|
|
nsAutoString topRowStr; topRowStr.Assign(NS_LITERAL_STRING("topRow"));
|
|
nsAutoString topRow;
|
|
topRow.AppendInt(mTopRowIndex);
|
|
box->SetProperty(topRowStr.get(), topRow.get());
|
|
}
|
|
|
|
// Always null out the cached outliner body frame.
|
|
nsAutoString outlinerBody(NS_LITERAL_STRING("outlinerbody"));
|
|
box->RemoveProperty(outlinerBody.get());
|
|
|
|
mOutlinerBoxObject = nsnull; // Drop our ref here.
|
|
}
|
|
|
|
mView = nsnull;
|
|
|
|
return nsLeafBoxFrame::Destroy(aPresContext);
|
|
}
|
|
|
|
void
|
|
nsOutlinerBodyFrame::EnsureBoxObject()
|
|
{
|
|
if (!mOutlinerBoxObject) {
|
|
nsCOMPtr<nsIContent> parent;
|
|
GetBaseElement(getter_AddRefs(parent));
|
|
|
|
if (parent) {
|
|
nsCOMPtr<nsIDocument> parentDoc;
|
|
parent->GetDocument(*getter_AddRefs(parentDoc));
|
|
NS_ASSERTION(parentDoc, "element has no document!");
|
|
|
|
nsCOMPtr<nsIDOMNSDocument> nsDoc = do_QueryInterface(parentDoc);
|
|
nsCOMPtr<nsIBoxObject> box;
|
|
nsCOMPtr<nsIDOMElement> domElem = do_QueryInterface(parent);
|
|
nsDoc->GetBoxObjectFor(domElem, getter_AddRefs(box));
|
|
|
|
if (box) {
|
|
nsCOMPtr<nsIOutlinerBoxObject> outlinerBox(do_QueryInterface(box));
|
|
SetBoxObject(outlinerBox);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame::SetBounds(nsBoxLayoutState& aBoxLayoutState, const nsRect& aRect)
|
|
{
|
|
PRBool recompute = (aRect != mRect);
|
|
nsresult rv = nsLeafBoxFrame::SetBounds(aBoxLayoutState, aRect);
|
|
|
|
if (recompute) {
|
|
mInnerBox = GetInnerBox();
|
|
mPageCount = mRowHeight ? (mInnerBox.height / mRowHeight) : 0;
|
|
|
|
PRInt32 rowCount;
|
|
mView->GetRowCount(&rowCount);
|
|
PRInt32 lastPageTopRow = rowCount - mPageCount;
|
|
if (mTopRowIndex >= lastPageTopRow)
|
|
ScrollToRow(lastPageTopRow);
|
|
|
|
InvalidateScrollbar();
|
|
CheckVerticalOverflow(aBoxLayoutState.GetReflowState() != nsnull);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
static void
|
|
AdjustForBorderPadding(nsIStyleContext* aContext, nsRect& aRect)
|
|
{
|
|
nsMargin m(0,0,0,0);
|
|
nsStyleBorderPadding bPad;
|
|
aContext->GetBorderPaddingFor(bPad);
|
|
bPad.GetBorderPadding(m);
|
|
aRect.Deflate(m);
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::GetView(nsIOutlinerView * *aView)
|
|
{
|
|
*aView = mView;
|
|
NS_IF_ADDREF(*aView);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::SetView(nsIOutlinerView * aView)
|
|
{
|
|
// First clear out the old view.
|
|
EnsureBoxObject();
|
|
nsCOMPtr<nsIBoxObject> box = do_QueryInterface(mOutlinerBoxObject);
|
|
|
|
nsAutoString view(NS_LITERAL_STRING("view"));
|
|
|
|
if (mView) {
|
|
mView->SetOutliner(nsnull);
|
|
mView = nsnull;
|
|
box->RemoveProperty(view.get());
|
|
|
|
// Only reset the top row index and delete the columns if we had an old non-null view.
|
|
mTopRowIndex = 0;
|
|
delete mColumns;
|
|
mColumns = nsnull;
|
|
}
|
|
|
|
// Outliner, meet the view.
|
|
mView = aView;
|
|
|
|
// Changing the view causes us to refetch our data. This will
|
|
// necessarily entail a full invalidation of the outliner.
|
|
Invalidate();
|
|
|
|
if (mView) {
|
|
// View, meet the outliner.
|
|
mView->SetOutliner(mOutlinerBoxObject);
|
|
|
|
box->SetPropertyAsSupports(view.get(), mView);
|
|
|
|
// Give the view a new empty selection object to play with, but only if it
|
|
// doesn't have one already.
|
|
nsCOMPtr<nsIOutlinerSelection> sel;
|
|
mView->GetSelection(getter_AddRefs(sel));
|
|
if (!sel) {
|
|
NS_NewOutlinerSelection(mOutlinerBoxObject, getter_AddRefs(sel));
|
|
mView->SetSelection(sel);
|
|
}
|
|
|
|
// The scrollbar will need to be updated.
|
|
InvalidateScrollbar();
|
|
|
|
// Reset scrollbar position.
|
|
UpdateScrollbar();
|
|
|
|
CheckVerticalOverflow(PR_FALSE);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame::GetFocused(PRBool* aFocused)
|
|
{
|
|
*aFocused = mFocused;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame::SetFocused(PRBool aFocused)
|
|
{
|
|
if (mFocused != aFocused) {
|
|
mFocused = aFocused;
|
|
if (mView) {
|
|
nsCOMPtr<nsIOutlinerSelection> sel;
|
|
mView->GetSelection(getter_AddRefs(sel));
|
|
if (sel)
|
|
sel->InvalidateSelection();
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame::GetOutlinerBody(nsIDOMElement** aElement)
|
|
{
|
|
//NS_ASSERTION(mContent, "no content, see bug #104878");
|
|
if (!mContent)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
return mContent->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)aElement);
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::GetSelection(nsIOutlinerSelection** aSelection)
|
|
{
|
|
if (mView)
|
|
return mView->GetSelection(aSelection);
|
|
|
|
*aSelection = nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::GetRowHeight(PRInt32* _retval)
|
|
{
|
|
PRInt32 height = mRowHeight == 0 ? GetRowHeight() : mRowHeight;
|
|
|
|
float t2p;
|
|
mPresContext->GetTwipsToPixels(&t2p);
|
|
*_retval = NSToCoordRound((float) height * t2p);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::GetColumnIndex(const PRUnichar *aColID, PRInt32 *_retval)
|
|
{
|
|
*_retval = -1;
|
|
for (nsOutlinerColumn* currCol = mColumns; currCol; currCol = currCol->GetNext()) {
|
|
if (currCol->GetID().Equals(aColID)) {
|
|
*_retval = currCol->GetColIndex();
|
|
break;
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::GetFirstVisibleRow(PRInt32 *_retval)
|
|
{
|
|
*_retval = mTopRowIndex;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::GetLastVisibleRow(PRInt32 *_retval)
|
|
{
|
|
*_retval = mTopRowIndex + mPageCount;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::GetPageCount(PRInt32 *_retval)
|
|
{
|
|
*_retval = mPageCount;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::Invalidate()
|
|
{
|
|
if (!mRect.IsEmpty()) {
|
|
nsLeafBoxFrame::Invalidate(mPresContext, mRect, mDragSession ? PR_TRUE : PR_FALSE);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::InvalidateColumn(const PRUnichar *aColID)
|
|
{
|
|
nscoord currX = mInnerBox.x;
|
|
for (nsOutlinerColumn* currCol = mColumns; currCol && currX < mInnerBox.x+mInnerBox.width;
|
|
currCol = currCol->GetNext()) {
|
|
if (currCol->GetID().Equals(aColID)) {
|
|
nsRect columnRect(currX, mInnerBox.y, currCol->GetWidth(), mInnerBox.height);
|
|
nsLeafBoxFrame::Invalidate(mPresContext, columnRect, mDragSession ? PR_TRUE : PR_FALSE);
|
|
break;
|
|
}
|
|
currX += currCol->GetWidth();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::InvalidateRow(PRInt32 aIndex)
|
|
{
|
|
if (aIndex < mTopRowIndex || aIndex > mTopRowIndex + mPageCount + 1)
|
|
return NS_OK;
|
|
|
|
nsRect rowRect(mInnerBox.x, mInnerBox.y+mRowHeight*(aIndex-mTopRowIndex), mInnerBox.width, mRowHeight);
|
|
if (!rowRect.IsEmpty())
|
|
nsLeafBoxFrame::Invalidate(mPresContext, rowRect, mDragSession ? PR_TRUE : PR_FALSE);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::InvalidateCell(PRInt32 aIndex, const PRUnichar *aColID)
|
|
{
|
|
if (aIndex < mTopRowIndex || aIndex > mTopRowIndex + mPageCount + 1)
|
|
return NS_OK;
|
|
|
|
if (mImageGuard)
|
|
return NS_OK;
|
|
|
|
nscoord currX = mInnerBox.x;
|
|
nscoord yPos = mInnerBox.y+mRowHeight*(aIndex-mTopRowIndex);
|
|
for (nsOutlinerColumn* currCol = mColumns; currCol && currX < mInnerBox.x+mInnerBox.width;
|
|
currCol = currCol->GetNext()) {
|
|
|
|
if (currCol->GetID().Equals(aColID)) {
|
|
nsRect cellRect(currX, yPos, currCol->GetWidth(), mRowHeight);
|
|
nsLeafBoxFrame::Invalidate(mPresContext, cellRect, mDragSession ? PR_TRUE : PR_FALSE);
|
|
break;
|
|
}
|
|
currX += currCol->GetWidth();
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::InvalidatePrimaryCell(PRInt32 aIndex)
|
|
{
|
|
if (aIndex < mTopRowIndex || aIndex > mTopRowIndex + mPageCount + 1)
|
|
return NS_OK;
|
|
|
|
nscoord currX = mInnerBox.x;
|
|
nscoord yPos = mInnerBox.y+mRowHeight*(aIndex-mTopRowIndex);
|
|
for (nsOutlinerColumn* currCol = mColumns; currCol && currX < mInnerBox.x+mInnerBox.width;
|
|
currCol = currCol->GetNext()) {
|
|
|
|
if (currCol->IsPrimary()) {
|
|
nsRect cellRect(currX, yPos, currCol->GetWidth(), mRowHeight);
|
|
nsLeafBoxFrame::Invalidate(mPresContext, cellRect, mDragSession ? PR_TRUE : PR_FALSE);
|
|
break;
|
|
}
|
|
currX += currCol->GetWidth();
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::InvalidateRange(PRInt32 aStart, PRInt32 aEnd)
|
|
{
|
|
if (aStart == aEnd)
|
|
return InvalidateRow(aStart);
|
|
|
|
PRInt32 last;
|
|
GetLastVisibleRow(&last);
|
|
if (aEnd < mTopRowIndex || aStart > last)
|
|
return NS_OK;
|
|
|
|
if (aStart < mTopRowIndex)
|
|
aStart = mTopRowIndex;
|
|
|
|
if (aEnd > last)
|
|
aEnd = last;
|
|
|
|
nsRect rangeRect(mInnerBox.x, mInnerBox.y+mRowHeight*(aStart-mTopRowIndex), mInnerBox.width, mRowHeight*(aEnd-aStart+1));
|
|
nsLeafBoxFrame::Invalidate(mPresContext, rangeRect, mDragSession ? PR_TRUE : PR_FALSE);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsOutlinerBodyFrame::UpdateScrollbar()
|
|
{
|
|
// Update the scrollbar.
|
|
nsCOMPtr<nsIContent> scrollbarContent;
|
|
NS_ASSERTION(mScrollbar, "no scroll bar");
|
|
if (!mScrollbar)
|
|
return;
|
|
mScrollbar->GetContent(getter_AddRefs(scrollbarContent));
|
|
float t2p;
|
|
mPresContext->GetTwipsToPixels(&t2p);
|
|
nscoord rowHeightAsPixels = NSToCoordRound((float)mRowHeight*t2p);
|
|
|
|
nsAutoString curPos;
|
|
curPos.AppendInt(mTopRowIndex*rowHeightAsPixels);
|
|
scrollbarContent->SetAttr(kNameSpaceID_None, nsXULAtoms::curpos, curPos, PR_TRUE);
|
|
}
|
|
|
|
nsresult nsOutlinerBodyFrame::CheckVerticalOverflow(PRBool aInReflow)
|
|
{
|
|
PRBool verticalOverflowChanged = PR_FALSE;
|
|
|
|
PRInt32 rowCount;
|
|
mView->GetRowCount(&rowCount);
|
|
if (!mVerticalOverflow && rowCount > mPageCount) {
|
|
mVerticalOverflow = PR_TRUE;
|
|
verticalOverflowChanged = PR_TRUE;
|
|
}
|
|
else if (mVerticalOverflow && rowCount <= mPageCount) {
|
|
mVerticalOverflow = PR_FALSE;
|
|
verticalOverflowChanged = PR_TRUE;
|
|
}
|
|
|
|
if (verticalOverflowChanged) {
|
|
nsScrollPortEvent* event = new nsScrollPortEvent();
|
|
event->eventStructType = NS_SCROLLPORT_EVENT;
|
|
event->widget = nsnull;
|
|
event->orient = nsScrollPortEvent::vertical;
|
|
event->nativeMsg = nsnull;
|
|
event->message = mVerticalOverflow ? NS_SCROLLPORT_OVERFLOW : NS_SCROLLPORT_UNDERFLOW;
|
|
|
|
if (aInReflow) {
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
mPresContext->GetShell(getter_AddRefs(shell));
|
|
shell->PostDOMEvent(mContent, event);
|
|
}
|
|
else {
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
|
mContent->HandleDOMEvent(mPresContext, event, nsnull, NS_EVENT_FLAG_INIT, &status);
|
|
delete event;
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::InvalidateScrollbar()
|
|
{
|
|
if (!mScrollbar) {
|
|
// Try to find it.
|
|
nsCOMPtr<nsIContent> parContent;
|
|
GetBaseElement(getter_AddRefs(parContent));
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
mPresContext->GetShell(getter_AddRefs(shell));
|
|
nsIFrame* outlinerFrame;
|
|
shell->GetPrimaryFrameFor(parContent, &outlinerFrame);
|
|
if (outlinerFrame)
|
|
mScrollbar = InitScrollbarFrame(mPresContext, outlinerFrame, this);
|
|
}
|
|
|
|
NS_ASSERTION(mScrollbar, "no scroll bar");
|
|
if (!mScrollbar || !mView)
|
|
return NS_OK;
|
|
|
|
PRInt32 rowCount = 0;
|
|
mView->GetRowCount(&rowCount);
|
|
|
|
nsCOMPtr<nsIContent> scrollbar;
|
|
mScrollbar->GetContent(getter_AddRefs(scrollbar));
|
|
|
|
nsAutoString maxposStr;
|
|
|
|
float t2p;
|
|
mPresContext->GetTwipsToPixels(&t2p);
|
|
nscoord rowHeightAsPixels = NSToCoordRound((float)mRowHeight*t2p);
|
|
|
|
PRInt32 size = rowHeightAsPixels*(rowCount-mPageCount);
|
|
maxposStr.AppendInt(size);
|
|
scrollbar->SetAttr(kNameSpaceID_None, nsXULAtoms::maxpos, maxposStr, PR_TRUE);
|
|
|
|
// Also set our page increment and decrement.
|
|
nscoord pageincrement = mPageCount*rowHeightAsPixels;
|
|
nsAutoString pageStr;
|
|
pageStr.AppendInt(pageincrement);
|
|
scrollbar->SetAttr(kNameSpaceID_None, nsXULAtoms::pageincrement, pageStr, PR_TRUE);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
// Takes client x/y in pixels, converts them to twips, and massages them to be
|
|
// in our coordinate system.
|
|
void
|
|
nsOutlinerBodyFrame::AdjustEventCoordsToBoxCoordSpace (PRInt32 aX, PRInt32 aY, PRInt32* aResultX, PRInt32* aResultY)
|
|
{
|
|
// Convert our x and y coords to twips.
|
|
float pixelsToTwips = 0.0;
|
|
mPresContext->GetPixelsToTwips(&pixelsToTwips);
|
|
aX = NSToIntRound(aX * pixelsToTwips);
|
|
aY = NSToIntRound(aY * pixelsToTwips);
|
|
|
|
// Get our box object.
|
|
nsCOMPtr<nsIDocument> doc;
|
|
mContent->GetDocument(*getter_AddRefs(doc));
|
|
nsCOMPtr<nsIDOMNSDocument> nsDoc(do_QueryInterface(doc));
|
|
nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(mContent));
|
|
|
|
nsCOMPtr<nsIBoxObject> boxObject;
|
|
nsDoc->GetBoxObjectFor(elt, getter_AddRefs(boxObject));
|
|
|
|
PRInt32 x;
|
|
PRInt32 y;
|
|
boxObject->GetX(&x);
|
|
boxObject->GetY(&y);
|
|
|
|
x = NSToIntRound(x * pixelsToTwips);
|
|
y = NSToIntRound(y * pixelsToTwips);
|
|
|
|
// Take into account the parent's scroll offset, since clientX and clientY
|
|
// are relative to the viewport.
|
|
|
|
nsIView* parentView;
|
|
nsLeafBoxFrame::GetView(mPresContext, &parentView);
|
|
parentView->GetParent(parentView);
|
|
parentView->GetParent(parentView);
|
|
|
|
if (parentView) {
|
|
nsIScrollableView* scrollView = nsnull;
|
|
CallQueryInterface(parentView, &scrollView);
|
|
if (scrollView) {
|
|
nscoord scrollX = 0, scrollY = 0;
|
|
scrollView->GetScrollPosition(scrollX, scrollY);
|
|
x -= scrollX;
|
|
y -= scrollY;
|
|
}
|
|
}
|
|
|
|
// Adjust into our coordinate space.
|
|
x = aX-x;
|
|
y = aY-y;
|
|
|
|
// Adjust y by the inner box y, so that we're in the inner box's
|
|
// coordinate space.
|
|
y += mInnerBox.y;
|
|
|
|
*aResultX = x;
|
|
*aResultY = y;
|
|
} // AdjustEventCoordsToBoxCoordSpace
|
|
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::GetCellAt(PRInt32 aX, PRInt32 aY, PRInt32* aRow, PRUnichar** aColID,
|
|
PRUnichar** aChildElt)
|
|
{
|
|
if (!mView)
|
|
return NS_OK;
|
|
|
|
// Ensure we have a row height.
|
|
if (mRowHeight == 0)
|
|
mRowHeight = GetRowHeight();
|
|
|
|
PRInt32 x, y;
|
|
AdjustEventCoordsToBoxCoordSpace ( aX, aY, &x, &y );
|
|
|
|
// Now just mod by our total inner box height and add to our top row index.
|
|
*aRow = (y/mRowHeight)+mTopRowIndex;
|
|
|
|
// Check if the coordinates are actually in our space.
|
|
PRInt32 rowCount;
|
|
mView->GetRowCount(&rowCount);
|
|
if (*aRow < 0 || *aRow >= rowCount) {
|
|
*aRow = -1;
|
|
return NS_OK;
|
|
}
|
|
|
|
// Determine the column hit.
|
|
nscoord currX = mInnerBox.x;
|
|
for (nsOutlinerColumn* currCol = mColumns; currCol && currX < mInnerBox.x+mInnerBox.width;
|
|
currCol = currCol->GetNext()) {
|
|
nsRect cellRect(currX, mInnerBox.y+mRowHeight*(*aRow-mTopRowIndex), currCol->GetWidth(), mRowHeight);
|
|
PRInt32 overflow = cellRect.x+cellRect.width-(mInnerBox.x+mInnerBox.width);
|
|
if (overflow > 0)
|
|
cellRect.width -= overflow;
|
|
|
|
if (x >= cellRect.x && x < cellRect.x + cellRect.width) {
|
|
// We know the column hit now.
|
|
*aColID = ToNewUnicode(currCol->GetID());
|
|
|
|
if (currCol->IsCycler())
|
|
// Cyclers contain only images. Fill this in immediately and return.
|
|
*aChildElt = ToNewUnicode(NS_LITERAL_STRING("image"));
|
|
else
|
|
GetItemWithinCellAt(x, cellRect, *aRow, currCol, aChildElt);
|
|
break;
|
|
}
|
|
|
|
currX += cellRect.width;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
//
|
|
// GetCoordsForCellItem
|
|
//
|
|
// Find the x/y location and width/height (all in PIXELS) of the given object
|
|
// in the given column.
|
|
//
|
|
// XXX IMPORTANT XXX:
|
|
// Hyatt says in the bug for this, that the following needs to be done:
|
|
// (1) You need to deal with overflow when computing cell rects. See other column
|
|
// iteration examples... if you don't deal with this, you'll mistakenly extend the
|
|
// cell into the scrollbar's rect.
|
|
//
|
|
// (2) You are adjusting the cell rect by the *row" border padding. That's
|
|
// wrong. You need to first adjust a row rect by its border/padding, and then the
|
|
// cell rect fits inside the adjusted row rect. It also can have border/padding
|
|
// as well as margins. The vertical direction isn't that important, but you need
|
|
// to get the horizontal direction right.
|
|
//
|
|
// (3) GetImageSize() does not include margins (but it does include border/padding).
|
|
// You need to make sure to add in the image's margins as well.
|
|
//
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame::GetCoordsForCellItem(PRInt32 aRow, const PRUnichar *aColID, const PRUnichar *aCellItem,
|
|
PRInt32 *aX, PRInt32 *aY, PRInt32 *aWidth, PRInt32 *aHeight)
|
|
{
|
|
*aX = 0;
|
|
*aY = 0;
|
|
*aWidth = 0;
|
|
*aHeight = 0;
|
|
|
|
nscoord currX = mInnerBox.x;
|
|
|
|
// The Rect for the requested item.
|
|
nsRect theRect;
|
|
|
|
for (nsOutlinerColumn* currCol = mColumns; currCol && currX < mInnerBox.x + mInnerBox.width;
|
|
currCol = currCol->GetNext()) {
|
|
|
|
// The Rect for the current cell.
|
|
nsRect cellRect(currX, mInnerBox.y + mRowHeight * (aRow - mTopRowIndex), currCol->GetWidth(), mRowHeight);
|
|
|
|
// Check the ID of the current column to see if it matches. If it doesn't
|
|
// increment the current X value and continue to the next column.
|
|
if (!currCol->GetID().Equals(aColID)) {
|
|
currX += cellRect.width;
|
|
continue;
|
|
}
|
|
|
|
// Now obtain the properties for our cell.
|
|
PrefillPropertyArray(aRow, currCol);
|
|
mView->GetCellProperties(aRow, currCol->GetID().get(), mScratchArray);
|
|
|
|
nsCOMPtr<nsIStyleContext> rowContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinerrow, getter_AddRefs(rowContext));
|
|
|
|
// We don't want to consider any of the decorations that may be present
|
|
// on the current row, so we have to deflate the rect by the border and
|
|
// padding and offset its left and top coordinates appropriately.
|
|
AdjustForBorderPadding(rowContext, cellRect);
|
|
|
|
nsCOMPtr<nsIStyleContext> cellContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinercell, getter_AddRefs(cellContext));
|
|
|
|
nsAutoString cell(NS_LITERAL_STRING("cell"));
|
|
if (currCol->IsCycler() || cell.EqualsWithConversion(aCellItem)) {
|
|
// If the current Column is a Cycler, then the Rect is just the cell - the margins.
|
|
// Similarly, if we're just being asked for the cell rect, provide it.
|
|
|
|
theRect = cellRect;
|
|
const nsStyleMargin* cellMarginData = (const nsStyleMargin*) cellContext->GetStyleData(eStyleStruct_Margin);
|
|
nsMargin cellMargin;
|
|
cellMarginData->GetMargin(cellMargin);
|
|
theRect.Deflate(cellMargin);
|
|
break;
|
|
}
|
|
|
|
// Since we're not looking for the cell, and since the cell isn't a cycler,
|
|
// we're looking for some subcomponent, and now we need to subtract the
|
|
// borders and padding of the cell from cellRect so this does not
|
|
// interfere with our computations.
|
|
AdjustForBorderPadding(cellContext, cellRect);
|
|
|
|
// Now we'll start making our way across the cell, starting at the edge of
|
|
// the cell and proceeding until we hit the right edge. |cellX| is the
|
|
// working X value that we will increment as we crawl from left to right.
|
|
nscoord cellX = cellRect.x;
|
|
nscoord remainWidth = cellRect.width;
|
|
|
|
if (currCol->IsPrimary()) {
|
|
// If the current Column is a Primary, then we need to take into account the indentation
|
|
// and possibly a twisty.
|
|
|
|
// The amount of indentation is the indentation width (|mIndentation|) by the level.
|
|
PRInt32 level;
|
|
mView->GetLevel(aRow, &level);
|
|
cellX += mIndentation * level;
|
|
remainWidth -= mIndentation * level;
|
|
|
|
PRBool hasTwisty = PR_FALSE;
|
|
PRBool isContainer = PR_FALSE;
|
|
mView->IsContainer(aRow, &isContainer);
|
|
if (isContainer) {
|
|
PRBool isContainerEmpty = PR_FALSE;
|
|
mView->IsContainerEmpty(aRow, &isContainerEmpty);
|
|
if (!isContainerEmpty)
|
|
hasTwisty = PR_TRUE;
|
|
}
|
|
|
|
// Find the twisty rect by computing its size.
|
|
nsCOMPtr<nsIStyleContext> twistyContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinertwisty, getter_AddRefs(twistyContext));
|
|
|
|
// |GetImageSize| returns the rect of the twisty image, including the
|
|
// borders and padding.
|
|
nsRect twistyImageRect = GetImageSize(aRow, currCol->GetID().get(), twistyContext);
|
|
if (NS_LITERAL_STRING("twisty").Equals(aCellItem)) {
|
|
// If we're looking for the twisty Rect, just return the result of |GetImageSize|
|
|
theRect = twistyImageRect;
|
|
break;
|
|
}
|
|
|
|
// Now we need to add in the margins of the twisty element, so that we
|
|
// can find the offset of the next element in the cell.
|
|
const nsStyleMargin* twistyMarginData = (const nsStyleMargin*) twistyContext->GetStyleData(eStyleStruct_Margin);
|
|
nsMargin twistyMargin;
|
|
twistyMarginData->GetMargin(twistyMargin);
|
|
twistyImageRect.Inflate(twistyMargin);
|
|
|
|
// Adjust our working X value with the twisty width (image size, margins,
|
|
// borders, padding.
|
|
cellX += twistyImageRect.width;
|
|
}
|
|
|
|
// Cell Image
|
|
nsCOMPtr<nsIStyleContext> imageContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinerimage, getter_AddRefs(imageContext));
|
|
|
|
nsRect imageSize = GetImageSize(aRow, currCol->GetID().get(), imageContext);
|
|
if (NS_LITERAL_STRING("image").Equals(aCellItem)) {
|
|
theRect = imageSize;
|
|
theRect.x = cellX;
|
|
theRect.y = cellRect.y;
|
|
break;
|
|
}
|
|
|
|
// Increment cellX by the image width
|
|
cellX += imageSize.width;
|
|
|
|
// Cell Text
|
|
nsAutoString cellText;
|
|
mView->GetCellText(aRow, currCol->GetID().get(), cellText);
|
|
|
|
// Create a scratch rect to represent the text rectangle, with the current
|
|
// X and Y coords, and a guess at the width and height. The width is the
|
|
// remaining width we have left to traverse in the cell, which will be the
|
|
// widest possible value for the text rect, and the row height.
|
|
nsRect textRect(cellX, cellRect.y, remainWidth, mRowHeight);
|
|
|
|
// Measure the width of the text. If the width of the text is greater than
|
|
// the remaining width available, then we just assume that the text has
|
|
// been cropped and use the remaining rect as the text Rect. Otherwise,
|
|
// we add in borders and padding to the text dimension and give that back.
|
|
nsCOMPtr<nsIStyleContext> textContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinercelltext, getter_AddRefs(textContext));
|
|
|
|
const nsStyleFont* fontStyle = (const nsStyleFont*) textContext->GetStyleData(eStyleStruct_Font);
|
|
nsCOMPtr<nsIDeviceContext> dc;
|
|
|
|
mPresContext->GetDeviceContext(getter_AddRefs(dc));
|
|
nsCOMPtr<nsIFontMetrics> fm;
|
|
dc->GetMetricsFor(fontStyle->mFont, *getter_AddRefs(fm));
|
|
nscoord height;
|
|
fm->GetHeight(height);
|
|
|
|
nsStyleBorderPadding borderPadding;
|
|
textContext->GetBorderPaddingFor(borderPadding);
|
|
nsMargin bp(0,0,0,0);
|
|
borderPadding.GetBorderPadding(bp);
|
|
|
|
textRect.height = height + bp.top + bp.bottom;
|
|
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
mPresContext->GetShell(getter_AddRefs(shell));
|
|
nsCOMPtr<nsIRenderingContext> rc;
|
|
shell->CreateRenderingContext(this, getter_AddRefs(rc));
|
|
rc->SetFont(fm);
|
|
nscoord width;
|
|
rc->GetWidth(cellText, width);
|
|
|
|
nscoord totalTextWidth = width + bp.left + bp.right;
|
|
if (totalTextWidth < remainWidth) {
|
|
// If the text is not cropped, the text is smaller than the available
|
|
// space and we set the text rect to be that width.
|
|
textRect.width = totalTextWidth;
|
|
}
|
|
|
|
theRect = textRect;
|
|
}
|
|
|
|
float t2p = 0.0;
|
|
mPresContext->GetTwipsToPixels(&t2p);
|
|
|
|
*aX = NSToIntRound(theRect.x * t2p);
|
|
*aY = NSToIntRound(theRect.y * t2p);
|
|
*aWidth = NSToIntRound(theRect.width * t2p);
|
|
*aHeight = NSToIntRound(theRect.height * t2p);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsOutlinerBodyFrame::GetItemWithinCellAt(PRInt32 aX, const nsRect& aCellRect,
|
|
PRInt32 aRowIndex,
|
|
nsOutlinerColumn* aColumn, PRUnichar** aChildElt)
|
|
{
|
|
// Obtain the properties for our cell.
|
|
PrefillPropertyArray(aRowIndex, aColumn);
|
|
mView->GetCellProperties(aRowIndex, aColumn->GetID().get(), mScratchArray);
|
|
|
|
// Resolve style for the cell.
|
|
nsCOMPtr<nsIStyleContext> cellContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinercell, getter_AddRefs(cellContext));
|
|
|
|
// Obtain the margins for the cell and then deflate our rect by that
|
|
// amount. The cell is assumed to be contained within the deflated rect.
|
|
nsRect cellRect(aCellRect);
|
|
const nsStyleMargin* cellMarginData = (const nsStyleMargin*)cellContext->GetStyleData(eStyleStruct_Margin);
|
|
nsMargin cellMargin;
|
|
cellMarginData->GetMargin(cellMargin);
|
|
cellRect.Deflate(cellMargin);
|
|
|
|
// Adjust the rect for its border and padding.
|
|
AdjustForBorderPadding(cellContext, cellRect);
|
|
|
|
if (aX < cellRect.x || aX >= cellRect.x + cellRect.width) {
|
|
// The user clicked within the cell's margins/borders/padding. This constitutes a click on the cell.
|
|
*aChildElt = ToNewUnicode(NS_LITERAL_STRING("cell"));
|
|
return NS_OK;
|
|
}
|
|
|
|
nscoord currX = cellRect.x;
|
|
nscoord remainingWidth = cellRect.width;
|
|
|
|
// XXX Handle right alignment hit testing.
|
|
|
|
if (aColumn->IsPrimary()) {
|
|
// If we're the primary column, we have indentation and a twisty.
|
|
PRInt32 level;
|
|
mView->GetLevel(aRowIndex, &level);
|
|
|
|
currX += mIndentation*level;
|
|
remainingWidth -= mIndentation*level;
|
|
|
|
if (aX < currX) {
|
|
// The user clicked within the indentation.
|
|
*aChildElt = ToNewUnicode(NS_LITERAL_STRING("cell"));
|
|
return NS_OK;
|
|
}
|
|
|
|
// Always leave space for the twisty.
|
|
nsRect twistyRect(currX, cellRect.y, remainingWidth, cellRect.height);
|
|
PRBool hasTwisty = PR_FALSE;
|
|
PRBool isContainer = PR_FALSE;
|
|
mView->IsContainer(aRowIndex, &isContainer);
|
|
if (isContainer) {
|
|
PRBool isContainerEmpty = PR_FALSE;
|
|
mView->IsContainerEmpty(aRowIndex, &isContainerEmpty);
|
|
if (!isContainerEmpty)
|
|
hasTwisty = PR_TRUE;
|
|
}
|
|
|
|
// Resolve style for the twisty.
|
|
nsCOMPtr<nsIStyleContext> twistyContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinertwisty, getter_AddRefs(twistyContext));
|
|
|
|
// We will treat a click as hitting the twisty if it happens on the margins, borders, padding,
|
|
// or content of the twisty object. By allowing a "slop" into the margin, we make it a little
|
|
// bit easier for a user to hit the twisty. (We don't want to be too picky here.)
|
|
nsRect imageSize = GetImageSize(aRowIndex, aColumn->GetID().get(), twistyContext);
|
|
const nsStyleMargin* twistyMarginData = (const nsStyleMargin*)twistyContext->GetStyleData(eStyleStruct_Margin);
|
|
nsMargin twistyMargin;
|
|
twistyMarginData->GetMargin(twistyMargin);
|
|
imageSize.Inflate(twistyMargin);
|
|
twistyRect.width = imageSize.width;
|
|
|
|
// Now we test to see if aX is actually within the twistyRect. If it is, and if the item should
|
|
// have a twisty, then we return "twisty". If it is within the rect but we shouldn't have a twisty,
|
|
// then we return "cell".
|
|
if (aX >= twistyRect.x && aX < twistyRect.x + twistyRect.width) {
|
|
if (hasTwisty)
|
|
*aChildElt = ToNewUnicode(NS_LITERAL_STRING("twisty"));
|
|
else
|
|
*aChildElt = ToNewUnicode(NS_LITERAL_STRING("cell"));
|
|
return NS_OK;
|
|
}
|
|
|
|
currX += twistyRect.width;
|
|
remainingWidth -= twistyRect.width;
|
|
}
|
|
|
|
// Now test to see if the user hit the icon for the cell.
|
|
nsRect iconRect(currX, cellRect.y, remainingWidth, cellRect.height);
|
|
|
|
// Resolve style for the image.
|
|
nsCOMPtr<nsIStyleContext> imageContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinerimage, getter_AddRefs(imageContext));
|
|
|
|
nsRect iconSize = GetImageSize(aRowIndex, aColumn->GetID().get(), imageContext);
|
|
const nsStyleMargin* imageMarginData = (const nsStyleMargin*)imageContext->GetStyleData(eStyleStruct_Margin);
|
|
nsMargin imageMargin;
|
|
imageMarginData->GetMargin(imageMargin);
|
|
iconSize.Inflate(imageMargin);
|
|
iconRect.width = iconSize.width;
|
|
|
|
if (aX >= iconRect.x && aX < iconRect.x + iconRect.width) {
|
|
// The user clicked on the image.
|
|
*aChildElt = ToNewUnicode(NS_LITERAL_STRING("image"));
|
|
return NS_OK;
|
|
}
|
|
|
|
// Just assume "text".
|
|
// XXX For marquee selection, we'll have to make this more precise and do text measurement.
|
|
*aChildElt = ToNewUnicode(NS_LITERAL_STRING("text"));
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsOutlinerBodyFrame::GetCellWidth(PRInt32 aRow, const nsAString& aColID,
|
|
nscoord& aDesiredSize, nscoord& aCurrentSize)
|
|
{
|
|
nsOutlinerColumn* currCol = nsnull;
|
|
// Keep looping until we find a column with a matching Id.
|
|
for (currCol = mColumns; currCol; currCol = currCol->GetNext()) {
|
|
if (currCol->GetID().Equals(aColID))
|
|
break;
|
|
}
|
|
|
|
if (currCol) {
|
|
// The rect for the current cell.
|
|
nsRect cellRect(0, 0, currCol->GetWidth(), mRowHeight);
|
|
|
|
// Adjust borders and padding for the cell.
|
|
nsCOMPtr<nsIStyleContext> cellContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinercell, getter_AddRefs(cellContext));
|
|
nsMargin m(0,0,0,0);
|
|
nsStyleBorderPadding bPad;
|
|
cellContext->GetBorderPaddingFor(bPad);
|
|
bPad.GetBorderPadding(m);
|
|
|
|
aCurrentSize = cellRect.width;
|
|
aDesiredSize = m.left + m.right;
|
|
|
|
if (currCol->IsPrimary()) {
|
|
// If the current Column is a Primary, then we need to take into account
|
|
// the indentation and possibly a twisty.
|
|
|
|
// The amount of indentation is the indentation width (|mIndentation|) by the level.
|
|
PRInt32 level;
|
|
mView->GetLevel(aRow, &level);
|
|
aDesiredSize += mIndentation * level;
|
|
|
|
// Find the twisty rect by computing its size.
|
|
nsCOMPtr<nsIStyleContext> twistyContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinertwisty, getter_AddRefs(twistyContext));
|
|
|
|
// |GetImageSize| returns the rect of the twisty image, including the
|
|
// borders and padding.
|
|
nsRect twistyImageRect = GetImageSize(aRow, currCol->GetID().get(), twistyContext);
|
|
|
|
// Add in the margins of the twisty element.
|
|
const nsStyleMargin* twistyMarginData = (const nsStyleMargin*) twistyContext->GetStyleData(eStyleStruct_Margin);
|
|
nsMargin twistyMargin;
|
|
twistyMarginData->GetMargin(twistyMargin);
|
|
twistyImageRect.Inflate(twistyMargin);
|
|
|
|
aDesiredSize += twistyImageRect.width;
|
|
}
|
|
|
|
nsCOMPtr<nsIStyleContext> imageContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinerimage, getter_AddRefs(imageContext));
|
|
|
|
// Account for the width of the cell image.
|
|
nsRect imageSize = GetImageSize(aRow, currCol->GetID().get(), imageContext);
|
|
aDesiredSize += imageSize.width;
|
|
|
|
// Get the cell text.
|
|
nsAutoString cellText;
|
|
mView->GetCellText(aRow, currCol->GetID().get(), cellText);
|
|
|
|
nsCOMPtr<nsIStyleContext> textContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinercelltext, getter_AddRefs(textContext));
|
|
|
|
// Get the borders and padding for the text.
|
|
nsStyleBorderPadding borderPadding;
|
|
textContext->GetBorderPaddingFor(borderPadding);
|
|
nsMargin bp(0,0,0,0);
|
|
borderPadding.GetBorderPadding(bp);
|
|
|
|
// Get the font style for the text and pass it to the rendering context.
|
|
const nsStyleFont* fontStyle = (const nsStyleFont*) textContext->GetStyleData(eStyleStruct_Font);
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
mPresContext->GetShell(getter_AddRefs(shell));
|
|
nsCOMPtr<nsIRenderingContext> rc;
|
|
shell->CreateRenderingContext(this, getter_AddRefs(rc));
|
|
rc->SetFont(fontStyle->mFont);
|
|
|
|
// Get the width of the text itself
|
|
nscoord width;
|
|
rc->GetWidth(cellText, width);
|
|
nscoord totalTextWidth = width + bp.left + bp.right;
|
|
aDesiredSize += totalTextWidth;
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame::IsCellCropped(PRInt32 aRow, const nsAString& aColID, PRBool *_retval)
|
|
{
|
|
nscoord currentSize, desiredSize;
|
|
GetCellWidth(aRow, aColID, desiredSize, currentSize);
|
|
*_retval = desiredSize > currentSize;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::RowCountChanged(PRInt32 aIndex, PRInt32 aCount)
|
|
{
|
|
if (aCount == 0 || !mView)
|
|
return NS_OK; // Nothing to do.
|
|
|
|
PRInt32 count = aCount > 0 ? aCount : -aCount;
|
|
PRInt32 rowCount;
|
|
mView->GetRowCount(&rowCount);
|
|
|
|
// Adjust our selection.
|
|
nsCOMPtr<nsIOutlinerSelection> sel;
|
|
mView->GetSelection(getter_AddRefs(sel));
|
|
if (sel)
|
|
sel->AdjustSelection(aIndex, aCount);
|
|
|
|
PRInt32 last;
|
|
GetLastVisibleRow(&last);
|
|
if (aIndex >= mTopRowIndex && aIndex <= last)
|
|
InvalidateRange(aIndex, last);
|
|
|
|
if (mTopRowIndex == 0) {
|
|
// Just update the scrollbar and return.
|
|
InvalidateScrollbar();
|
|
CheckVerticalOverflow(PR_FALSE);
|
|
return NS_OK;
|
|
}
|
|
|
|
// Adjust our top row index.
|
|
if (aCount > 0) {
|
|
if (mTopRowIndex > aIndex) {
|
|
// Rows came in above us. Augment the top row index.
|
|
mTopRowIndex += aCount;
|
|
UpdateScrollbar();
|
|
}
|
|
}
|
|
else if (aCount < 0) {
|
|
if (mTopRowIndex > aIndex+count-1) {
|
|
// No need to invalidate. The remove happened
|
|
// completely above us (offscreen).
|
|
mTopRowIndex -= count;
|
|
UpdateScrollbar();
|
|
}
|
|
else if (mTopRowIndex >= aIndex) {
|
|
// This is a full-blown invalidate.
|
|
if (mTopRowIndex + mPageCount > rowCount - 1)
|
|
mTopRowIndex = PR_MAX(0, rowCount - 1 - mPageCount);
|
|
UpdateScrollbar();
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
InvalidateScrollbar();
|
|
CheckVerticalOverflow(PR_FALSE);
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsOutlinerBodyFrame::PrefillPropertyArray(PRInt32 aRowIndex, nsOutlinerColumn* aCol)
|
|
{
|
|
mScratchArray->Clear();
|
|
|
|
// focus
|
|
if (mFocused)
|
|
mScratchArray->AppendElement(nsXULAtoms::focus);
|
|
|
|
// sort
|
|
PRBool sorted = PR_FALSE;
|
|
mView->IsSorted(&sorted);
|
|
if (sorted)
|
|
mScratchArray->AppendElement(nsXULAtoms::sorted);
|
|
|
|
if (aRowIndex != -1) {
|
|
nsCOMPtr<nsIOutlinerSelection> selection;
|
|
mView->GetSelection(getter_AddRefs(selection));
|
|
|
|
if (selection) {
|
|
// selected
|
|
PRBool isSelected;
|
|
selection->IsSelected(aRowIndex, &isSelected);
|
|
if (isSelected)
|
|
mScratchArray->AppendElement(nsHTMLAtoms::selected);
|
|
|
|
// current
|
|
PRInt32 currentIndex;
|
|
selection->GetCurrentIndex(¤tIndex);
|
|
if (aRowIndex == currentIndex)
|
|
mScratchArray->AppendElement(nsXULAtoms::current);
|
|
}
|
|
|
|
// container or leaf
|
|
PRBool isContainer = PR_FALSE;
|
|
mView->IsContainer(aRowIndex, &isContainer);
|
|
if (isContainer) {
|
|
mScratchArray->AppendElement(nsXULAtoms::container);
|
|
|
|
// open or closed
|
|
PRBool isOpen = PR_FALSE;
|
|
mView->IsContainerOpen(aRowIndex, &isOpen);
|
|
if (isOpen)
|
|
mScratchArray->AppendElement(nsXULAtoms::open);
|
|
else
|
|
mScratchArray->AppendElement(nsXULAtoms::closed);
|
|
}
|
|
else {
|
|
mScratchArray->AppendElement(nsXULAtoms::leaf);
|
|
}
|
|
|
|
// drop feedback
|
|
if (mDropAllowed && mDropRow == aRowIndex) {
|
|
if (mDropOrient == nsIOutlinerView::inDropBefore)
|
|
mScratchArray->AppendElement(nsXULAtoms::dropBefore);
|
|
else if (mDropOrient == nsIOutlinerView::inDropOn)
|
|
mScratchArray->AppendElement(nsXULAtoms::drop);
|
|
else if (mDropOrient == nsIOutlinerView::inDropAfter)
|
|
mScratchArray->AppendElement(nsXULAtoms::dropAfter);
|
|
}
|
|
}
|
|
|
|
if (aCol) {
|
|
nsCOMPtr<nsIAtom> colID;
|
|
aCol->GetIDAtom(getter_AddRefs(colID));
|
|
mScratchArray->AppendElement(colID);
|
|
|
|
if (aCol->IsPrimary())
|
|
mScratchArray->AppendElement(nsXULAtoms::primary);
|
|
}
|
|
}
|
|
|
|
#ifdef USE_IMG2
|
|
nsresult
|
|
nsOutlinerBodyFrame::GetImage(PRInt32 aRowIndex, const PRUnichar* aColID,
|
|
nsIStyleContext* aStyleContext, imgIContainer** aResult)
|
|
{
|
|
*aResult = nsnull;
|
|
if (mImageCache) {
|
|
// Look the image up in our cache.
|
|
nsISupportsKey key(aStyleContext);
|
|
nsCOMPtr<imgIRequest> imgReq = getter_AddRefs(NS_STATIC_CAST(imgIRequest*, mImageCache->Get(&key)));
|
|
if (imgReq) {
|
|
// Find out if the image has loaded.
|
|
PRUint32 status;
|
|
imgReq->GetImageStatus(&status);
|
|
imgReq->GetImage(aResult); // We hand back the image here. The GetImage call addrefs *aResult.
|
|
PRUint32 numFrames = 1;
|
|
if (*aResult)
|
|
(*aResult)->GetNumFrames(&numFrames);
|
|
|
|
if ((!(status & imgIRequest::STATUS_LOAD_COMPLETE)) || numFrames > 1) {
|
|
// We either aren't done loading, or we're animating. Add our row as a listener for invalidations.
|
|
nsCOMPtr<imgIDecoderObserver> obs;
|
|
imgReq->GetDecoderObserver(getter_AddRefs(obs));
|
|
nsCOMPtr<nsIOutlinerImageListener> listener(do_QueryInterface(obs));
|
|
if (listener)
|
|
listener->AddRow(aRowIndex);
|
|
return NS_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!*aResult) {
|
|
// We missed. Kick off the image load.
|
|
// Obtain the URL from the style context.
|
|
const nsStyleList* myList =
|
|
(const nsStyleList*)aStyleContext->GetStyleData(eStyleStruct_List);
|
|
|
|
if (myList->mListStyleImage.Length() > 0) {
|
|
// Create a new nsOutlinerImageListener object and pass it our row and column
|
|
// information.
|
|
nsOutlinerImageListener* listener = new nsOutlinerImageListener(mOutlinerBoxObject, aColID);
|
|
if (!listener)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
listener->AddRow(aRowIndex);
|
|
|
|
nsCOMPtr<nsIURI> baseURI;
|
|
nsCOMPtr<nsIDocument> doc;
|
|
mContent->GetDocument(*getter_AddRefs(doc));
|
|
doc->GetBaseURL(*getter_AddRefs(baseURI));
|
|
|
|
nsCOMPtr<nsIURI> srcURI;
|
|
NS_NewURI(getter_AddRefs(srcURI), myList->mListStyleImage, baseURI);
|
|
nsCOMPtr<imgIRequest> imageRequest;
|
|
|
|
nsresult rv;
|
|
nsCOMPtr<imgILoader> il(do_GetService("@mozilla.org/image/loader;1", &rv));
|
|
mImageGuard = PR_TRUE;
|
|
il->LoadImage(srcURI, nsnull, listener, mPresContext, nsIRequest::LOAD_NORMAL, nsnull, nsnull, getter_AddRefs(imageRequest));
|
|
mImageGuard = PR_FALSE;
|
|
|
|
// In a case it was already cached.
|
|
imageRequest->GetImage(aResult);
|
|
|
|
if (!mImageCache) {
|
|
mImageCache = new nsSupportsHashtable(64);
|
|
if (!mImageCache)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
nsISupportsKey key(aStyleContext);
|
|
mImageCache->Put(&key, imageRequest);
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
#endif
|
|
|
|
nsRect nsOutlinerBodyFrame::GetImageSize(PRInt32 aRowIndex, const PRUnichar* aColID,
|
|
nsIStyleContext* aStyleContext)
|
|
{
|
|
// XXX We should respond to visibility rules for collapsed vs. hidden.
|
|
|
|
// This method returns the width of the twisty INCLUDING borders and padding.
|
|
// It first checks the style context for a width. If none is found, it tries to
|
|
// use the default image width for the twisty. If no image is found, it defaults
|
|
// to border+padding.
|
|
nsRect r(0,0,0,0);
|
|
nsMargin m(0,0,0,0);
|
|
nsStyleBorderPadding bPad;
|
|
aStyleContext->GetBorderPaddingFor(bPad);
|
|
bPad.GetBorderPadding(m);
|
|
r.Inflate(m);
|
|
|
|
// Now r contains our border+padding info. We now need to get our width and
|
|
// height.
|
|
PRBool needWidth = PR_FALSE;
|
|
PRBool needHeight = PR_FALSE;
|
|
|
|
const nsStylePosition* myPosition = (const nsStylePosition*)
|
|
aStyleContext->GetStyleData(eStyleStruct_Position);
|
|
const nsStyleList* myList = (const nsStyleList*)
|
|
aStyleContext->GetStyleData(eStyleStruct_List);
|
|
|
|
r.x += myList->mImageRegion.x;
|
|
r.y += myList->mImageRegion.y;
|
|
|
|
if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord) {
|
|
PRInt32 val = myPosition->mWidth.GetCoordValue();
|
|
r.width += val;
|
|
}
|
|
else if (myList->mImageRegion.width > 0)
|
|
r.width += myList->mImageRegion.width;
|
|
else
|
|
needWidth = PR_TRUE;
|
|
|
|
if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord) {
|
|
PRInt32 val = myPosition->mHeight.GetCoordValue();
|
|
r.height += val;
|
|
}
|
|
else if (myList->mImageRegion.height > 0)
|
|
r.height += myList->mImageRegion.height;
|
|
else
|
|
needHeight = PR_TRUE;
|
|
|
|
#ifdef USE_IMG2
|
|
// We have to load image even though we already have a size.
|
|
// Don't change this, otherwise things start to go crazy.
|
|
nsCOMPtr<imgIContainer> image;
|
|
GetImage(aRowIndex, aColID, aStyleContext, getter_AddRefs(image));
|
|
if (image) {
|
|
if (needWidth || needHeight) {
|
|
// Get the natural image size.
|
|
float p2t;
|
|
mPresContext->GetPixelsToTwips(&p2t);
|
|
|
|
if (needWidth) {
|
|
// Get the size from the image.
|
|
nscoord width;
|
|
image->GetWidth(&width);
|
|
r.width += NSIntPixelsToTwips(width, p2t);
|
|
}
|
|
|
|
if (needHeight) {
|
|
nscoord height;
|
|
image->GetHeight(&height);
|
|
r.height += NSIntPixelsToTwips(height, p2t);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
PRInt32 nsOutlinerBodyFrame::GetRowHeight()
|
|
{
|
|
// Look up the correct height. It is equal to the specified height
|
|
// + the specified margins.
|
|
nsCOMPtr<nsIStyleContext> rowContext;
|
|
mScratchArray->Clear();
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinerrow, getter_AddRefs(rowContext));
|
|
if (rowContext) {
|
|
const nsStylePosition* myPosition = (const nsStylePosition*)
|
|
rowContext->GetStyleData(eStyleStruct_Position);
|
|
if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord) {
|
|
PRInt32 val = myPosition->mHeight.GetCoordValue();
|
|
if (val > 0) {
|
|
// XXX Check box-sizing to determine if border/padding should augment the height
|
|
// Inflate the height by our margins.
|
|
nsRect rowRect(0,0,0,val);
|
|
const nsStyleMargin* rowMarginData = (const nsStyleMargin*)rowContext->GetStyleData(eStyleStruct_Margin);
|
|
nsMargin rowMargin;
|
|
rowMarginData->GetMargin(rowMargin);
|
|
rowRect.Inflate(rowMargin);
|
|
val = rowRect.height;
|
|
}
|
|
return val;
|
|
}
|
|
}
|
|
float p2t;
|
|
mPresContext->GetPixelsToTwips(&p2t);
|
|
return NSIntPixelsToTwips(19, p2t); // As good a default as any.
|
|
}
|
|
|
|
PRInt32 nsOutlinerBodyFrame::GetIndentation()
|
|
{
|
|
// Look up the correct indentation. It is equal to the specified indentation width.
|
|
nsCOMPtr<nsIStyleContext> indentContext;
|
|
mScratchArray->Clear();
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinerindentation, getter_AddRefs(indentContext));
|
|
if (indentContext) {
|
|
const nsStylePosition* myPosition = (const nsStylePosition*)
|
|
indentContext->GetStyleData(eStyleStruct_Position);
|
|
if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord) {
|
|
PRInt32 val = myPosition->mWidth.GetCoordValue();
|
|
return val;
|
|
}
|
|
}
|
|
float p2t;
|
|
mPresContext->GetPixelsToTwips(&p2t);
|
|
return NSIntPixelsToTwips(16, p2t); // As good a default as any.
|
|
}
|
|
|
|
nsRect nsOutlinerBodyFrame::GetInnerBox()
|
|
{
|
|
nsRect r(0,0,mRect.width, mRect.height);
|
|
nsMargin m(0,0,0,0);
|
|
nsStyleBorderPadding bPad;
|
|
mStyleContext->GetBorderPaddingFor(bPad);
|
|
bPad.GetBorderPadding(m);
|
|
r.Deflate(m);
|
|
return r;
|
|
}
|
|
|
|
nsLineStyle nsOutlinerBodyFrame::ConvertBorderStyleToLineStyle(PRUint8 aBorderStyle)
|
|
{
|
|
switch (aBorderStyle) {
|
|
case NS_STYLE_BORDER_STYLE_DOTTED:
|
|
return nsLineStyle_kDotted;
|
|
case NS_STYLE_BORDER_STYLE_DASHED:
|
|
return nsLineStyle_kDashed;
|
|
default:
|
|
return nsLineStyle_kSolid;
|
|
}
|
|
}
|
|
|
|
// Painting routines
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::Paint(nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nsFramePaintLayer aWhichLayer,
|
|
PRUint32 aFlags)
|
|
{
|
|
// XXX This trap handles an odd bogus 1 pixel invalidation that we keep getting
|
|
// when scrolling.
|
|
if (aDirtyRect.width == 1)
|
|
return NS_OK;
|
|
|
|
if (aWhichLayer != NS_FRAME_PAINT_LAYER_BACKGROUND &&
|
|
aWhichLayer != NS_FRAME_PAINT_LAYER_FOREGROUND)
|
|
return NS_OK;
|
|
|
|
const nsStyleVisibility* vis =
|
|
(const nsStyleVisibility*)mStyleContext->GetStyleData(eStyleStruct_Visibility);
|
|
if (!vis->IsVisibleOrCollapsed())
|
|
return NS_OK; // We're invisible. Don't paint.
|
|
|
|
// Handles painting our background, border, and outline.
|
|
nsresult rv = nsLeafFrame::Paint(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (!mView)
|
|
return NS_OK;
|
|
|
|
PRBool clipState = PR_FALSE;
|
|
|
|
// Update our page count, our available height and our row height.
|
|
PRInt32 oldRowHeight = mRowHeight;
|
|
PRInt32 oldPageCount = mPageCount;
|
|
mRowHeight = GetRowHeight();
|
|
mIndentation = GetIndentation();
|
|
mInnerBox = GetInnerBox();
|
|
mPageCount = mInnerBox.height/mRowHeight;
|
|
|
|
if (mRowHeight != oldRowHeight || oldPageCount != mPageCount) {
|
|
// Schedule a ResizeReflow that will update our page count properly.
|
|
nsBoxLayoutState state(mPresContext);
|
|
MarkDirty(state);
|
|
}
|
|
|
|
PRInt32 rowCount = 0;
|
|
mView->GetRowCount(&rowCount);
|
|
|
|
// Ensure our column info is built.
|
|
EnsureColumns();
|
|
|
|
// Loop through our columns and paint them (e.g., for sorting). This is only
|
|
// relevant when painting backgrounds, since columns contain no content. Content
|
|
// is contained in the rows.
|
|
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
|
|
nscoord currX = mInnerBox.x;
|
|
for (nsOutlinerColumn* currCol = mColumns; currCol && currX < mInnerBox.x+mInnerBox.width;
|
|
currCol = currCol->GetNext()) {
|
|
nsRect colRect(currX, mInnerBox.y, currCol->GetWidth(), mInnerBox.height);
|
|
PRInt32 overflow = colRect.x+colRect.width-(mInnerBox.x+mInnerBox.width);
|
|
if (overflow > 0)
|
|
colRect.width -= overflow;
|
|
nsRect dirtyRect;
|
|
if (dirtyRect.IntersectRect(aDirtyRect, colRect)) {
|
|
PaintColumn(currCol, colRect, aPresContext, aRenderingContext, aDirtyRect, aWhichLayer);
|
|
}
|
|
currX += currCol->GetWidth();
|
|
}
|
|
}
|
|
|
|
// Loop through our on-screen rows.
|
|
for (PRInt32 i = mTopRowIndex; i < rowCount && i < mTopRowIndex+mPageCount+1; i++) {
|
|
nsRect rowRect(mInnerBox.x, mInnerBox.y+mRowHeight*(i-mTopRowIndex), mInnerBox.width, mRowHeight);
|
|
nsRect dirtyRect;
|
|
if (dirtyRect.IntersectRect(aDirtyRect, rowRect) && rowRect.y < (mInnerBox.y+mInnerBox.height)) {
|
|
PRBool clip = (rowRect.y + rowRect.height > mInnerBox.y + mInnerBox.height);
|
|
if (clip) {
|
|
// We need to clip the last row, since it extends outside our inner box. Push
|
|
// a clip rect down.
|
|
PRInt32 overflow = (rowRect.y+rowRect.height) - (mInnerBox.y+mInnerBox.height);
|
|
nsRect clipRect(rowRect.x, rowRect.y, mInnerBox.width, mRowHeight-overflow);
|
|
aRenderingContext.PushState();
|
|
aRenderingContext.SetClipRect(clipRect, nsClipCombine_kReplace, clipState);
|
|
}
|
|
|
|
PaintRow(i, rowRect, aPresContext, aRenderingContext, aDirtyRect, aWhichLayer);
|
|
|
|
if (clip)
|
|
aRenderingContext.PopState(clipState);
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::PaintColumn(nsOutlinerColumn* aColumn,
|
|
const nsRect& aColRect,
|
|
nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nsFramePaintLayer aWhichLayer)
|
|
{
|
|
if (aColRect.width == 0)
|
|
return NS_OK; // Don't paint hidden columns.
|
|
|
|
// Now obtain the properties for our cell.
|
|
// XXX Automatically fill in the following props: open, closed, container, leaf, selected, focused, and the col ID.
|
|
PrefillPropertyArray(-1, aColumn);
|
|
nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(aColumn->GetElement()));
|
|
mView->GetColumnProperties(aColumn->GetID().get(), elt, mScratchArray);
|
|
|
|
// Read special properties from attributes on the column content node
|
|
nsAutoString attr;
|
|
aColumn->GetElement()->GetAttr(kNameSpaceID_None, nsXULAtoms::insertbefore, attr);
|
|
if (attr.Equals(NS_LITERAL_STRING("true")))
|
|
mScratchArray->AppendElement(nsXULAtoms::insertbefore);
|
|
attr.Assign(NS_LITERAL_STRING(""));
|
|
aColumn->GetElement()->GetAttr(kNameSpaceID_None, nsXULAtoms::insertafter, attr);
|
|
if (attr.Equals(NS_LITERAL_STRING("true")))
|
|
mScratchArray->AppendElement(nsXULAtoms::insertafter);
|
|
|
|
// Resolve style for the column. It contains all the info we need to lay ourselves
|
|
// out and to paint.
|
|
nsCOMPtr<nsIStyleContext> colContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinercolumn, getter_AddRefs(colContext));
|
|
|
|
// Obtain the margins for the cell and then deflate our rect by that
|
|
// amount. The cell is assumed to be contained within the deflated rect.
|
|
nsRect colRect(aColRect);
|
|
const nsStyleMargin* colMarginData = (const nsStyleMargin*)colContext->GetStyleData(eStyleStruct_Margin);
|
|
nsMargin colMargin;
|
|
colMarginData->GetMargin(colMargin);
|
|
colRect.Deflate(colMargin);
|
|
|
|
PaintBackgroundLayer(colContext, aPresContext, aRenderingContext, colRect, aDirtyRect);
|
|
|
|
return NS_OK;
|
|
}
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::PaintRow(int aRowIndex, const nsRect& aRowRect,
|
|
nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nsFramePaintLayer aWhichLayer)
|
|
{
|
|
// We have been given a rect for our row. We treat this row like a full-blown
|
|
// frame, meaning that it can have borders, margins, padding, and a background.
|
|
|
|
// Without a view, we have no data. Check for this up front.
|
|
if (!mView)
|
|
return NS_OK;
|
|
|
|
// Now obtain the properties for our row.
|
|
// XXX Automatically fill in the following props: open, closed, container, leaf, selected, focused
|
|
PrefillPropertyArray(aRowIndex, nsnull);
|
|
mView->GetRowProperties(aRowIndex, mScratchArray);
|
|
|
|
// Resolve style for the row. It contains all the info we need to lay ourselves
|
|
// out and to paint.
|
|
nsCOMPtr<nsIStyleContext> rowContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinerrow, getter_AddRefs(rowContext));
|
|
|
|
// Obtain the margins for the row and then deflate our rect by that
|
|
// amount. The row is assumed to be contained within the deflated rect.
|
|
nsRect rowRect(aRowRect);
|
|
const nsStyleMargin* rowMarginData = (const nsStyleMargin*)rowContext->GetStyleData(eStyleStruct_Margin);
|
|
nsMargin rowMargin;
|
|
rowMarginData->GetMargin(rowMargin);
|
|
rowRect.Deflate(rowMargin);
|
|
|
|
// If the layer is the background layer, we must paint our borders and background for our
|
|
// row rect. If a -moz-appearance is provided, use theme drawing only if the current row
|
|
// is not selected (since we draw the selection as part of drawing the background).
|
|
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
|
|
PRBool useTheme = PR_FALSE;
|
|
nsCOMPtr<nsITheme> theme;
|
|
const nsStyleDisplay* displayData = (const nsStyleDisplay*)rowContext->GetStyleData(eStyleStruct_Display);
|
|
if ( displayData->mAppearance ) {
|
|
aPresContext->GetTheme(getter_AddRefs(theme));
|
|
if (theme && theme->ThemeSupportsWidget(aPresContext, displayData->mAppearance))
|
|
useTheme = PR_TRUE;
|
|
}
|
|
PRBool isSelected = PR_FALSE;
|
|
nsCOMPtr<nsIOutlinerSelection> selection;
|
|
GetSelection(getter_AddRefs(selection));
|
|
if ( selection )
|
|
selection->IsSelected(aRowIndex, &isSelected);
|
|
if ( useTheme && !isSelected )
|
|
theme->DrawWidgetBackground(&aRenderingContext, this,
|
|
displayData->mAppearance, rowRect, aDirtyRect);
|
|
else
|
|
PaintBackgroundLayer(rowContext, aPresContext, aRenderingContext, rowRect, aDirtyRect);
|
|
}
|
|
|
|
// Adjust the rect for its border and padding.
|
|
AdjustForBorderPadding(rowContext, rowRect);
|
|
|
|
PRBool isSeparator = PR_FALSE;
|
|
mView->IsSeparator(aRowIndex, &isSeparator);
|
|
if (isSeparator) {
|
|
// The row is a separator. Paint only a double horizontal line.
|
|
|
|
// Resolve style for the separator.
|
|
nsCOMPtr<nsIStyleContext> separatorContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinerseparator, getter_AddRefs(separatorContext));
|
|
|
|
// Get border style
|
|
const nsStyleBorder* borderStyle = (const nsStyleBorder*)separatorContext->GetStyleData(eStyleStruct_Border);
|
|
|
|
aRenderingContext.PushState();
|
|
|
|
PRUint8 side = NS_SIDE_TOP;
|
|
nscoord currY = rowRect.y + rowRect.height / 2;
|
|
for (PRInt32 i = 0; i < 2; i++) {
|
|
nscolor color;
|
|
PRBool transparent; PRBool foreground;
|
|
borderStyle->GetBorderColor(side, color, transparent, foreground);
|
|
aRenderingContext.SetColor(color);
|
|
PRUint8 style;
|
|
style = borderStyle->GetBorderStyle(side);
|
|
aRenderingContext.SetLineStyle(ConvertBorderStyleToLineStyle(style));
|
|
|
|
aRenderingContext.DrawLine(rowRect.x, currY, rowRect.x + rowRect.width, currY);
|
|
|
|
side = NS_SIDE_BOTTOM;
|
|
currY += 16;
|
|
}
|
|
|
|
PRBool clipState;
|
|
aRenderingContext.PopState(clipState);
|
|
}
|
|
else {
|
|
// Now loop over our cells. Only paint a cell if it intersects with our dirty rect.
|
|
nscoord currX = rowRect.x;
|
|
for (nsOutlinerColumn* currCol = mColumns; currCol && currX < mInnerBox.x+mInnerBox.width;
|
|
currCol = currCol->GetNext()) {
|
|
nsRect cellRect(currX, rowRect.y, currCol->GetWidth(), rowRect.height);
|
|
PRInt32 overflow = cellRect.x+cellRect.width-(mInnerBox.x+mInnerBox.width);
|
|
if (overflow > 0)
|
|
cellRect.width -= overflow;
|
|
nsRect dirtyRect;
|
|
if (dirtyRect.IntersectRect(aDirtyRect, cellRect)) {
|
|
PaintCell(aRowIndex, currCol, cellRect, aPresContext, aRenderingContext, aDirtyRect, aWhichLayer);
|
|
}
|
|
currX += currCol->GetWidth();
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::PaintCell(int aRowIndex,
|
|
nsOutlinerColumn* aColumn,
|
|
const nsRect& aCellRect,
|
|
nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nsFramePaintLayer aWhichLayer)
|
|
{
|
|
if (aCellRect.width == 0)
|
|
return NS_OK; // Don't paint cells in hidden columns.
|
|
|
|
// Now obtain the properties for our cell.
|
|
// XXX Automatically fill in the following props: open, closed, container, leaf, selected, focused, and the col ID.
|
|
PrefillPropertyArray(aRowIndex, aColumn);
|
|
mView->GetCellProperties(aRowIndex, aColumn->GetID().get(), mScratchArray);
|
|
|
|
// Resolve style for the cell. It contains all the info we need to lay ourselves
|
|
// out and to paint.
|
|
nsCOMPtr<nsIStyleContext> cellContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinercell, getter_AddRefs(cellContext));
|
|
|
|
// Obtain the margins for the cell and then deflate our rect by that
|
|
// amount. The cell is assumed to be contained within the deflated rect.
|
|
nsRect cellRect(aCellRect);
|
|
const nsStyleMargin* cellMarginData = (const nsStyleMargin*)cellContext->GetStyleData(eStyleStruct_Margin);
|
|
nsMargin cellMargin;
|
|
cellMarginData->GetMargin(cellMargin);
|
|
cellRect.Deflate(cellMargin);
|
|
|
|
// If the layer is the background layer, we must paint our borders and background for our
|
|
// row rect.
|
|
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer)
|
|
PaintBackgroundLayer(cellContext, aPresContext, aRenderingContext, cellRect, aDirtyRect);
|
|
|
|
// Adjust the rect for its border and padding.
|
|
AdjustForBorderPadding(cellContext, cellRect);
|
|
|
|
nscoord currX = cellRect.x;
|
|
nscoord remainingWidth = cellRect.width;
|
|
|
|
// Now we paint the contents of the cells.
|
|
// Text alignment determines the order in which we paint.
|
|
// LEFT means paint from left to right.
|
|
// RIGHT means paint from right to left.
|
|
// XXX Implement RIGHT alignment!
|
|
|
|
if (aColumn->IsPrimary()) {
|
|
// If we're the primary column, we need to indent and paint the twisty and any connecting lines
|
|
// between siblings.
|
|
|
|
PRInt32 level;
|
|
mView->GetLevel(aRowIndex, &level);
|
|
|
|
currX += mIndentation * level;
|
|
remainingWidth -= mIndentation * level;
|
|
|
|
// Resolve the style to use for the connecting lines.
|
|
nsCOMPtr<nsIStyleContext> lineContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinerline, getter_AddRefs(lineContext));
|
|
const nsStyleVisibility* vis =
|
|
(const nsStyleVisibility*)lineContext->GetStyleData(eStyleStruct_Visibility);
|
|
|
|
if (vis->IsVisibleOrCollapsed() && level && NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
|
|
// Paint the connecting lines.
|
|
|
|
// Get the size of the twisty. We don't want to paint the twisty
|
|
// before painting of connecting lines since it would paint lines over
|
|
// the twisty. But we need to leave a place for it.
|
|
nsCOMPtr<nsIStyleContext> twistyContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinertwisty, getter_AddRefs(twistyContext));
|
|
|
|
nsRect twistySize = GetImageSize(aRowIndex, aColumn->GetID().get(), twistyContext);
|
|
|
|
const nsStyleMargin* twistyMarginData = (const nsStyleMargin*)twistyContext->GetStyleData(eStyleStruct_Margin);
|
|
nsMargin twistyMargin;
|
|
twistyMarginData->GetMargin(twistyMargin);
|
|
twistySize.Inflate(twistyMargin);
|
|
|
|
aRenderingContext.PushState();
|
|
|
|
const nsStyleBorder* borderStyle = (const nsStyleBorder*)lineContext->GetStyleData(eStyleStruct_Border);
|
|
nscolor color;
|
|
PRBool transparent; PRBool foreground;
|
|
borderStyle->GetBorderColor(NS_SIDE_LEFT, color, transparent, foreground);
|
|
|
|
aRenderingContext.SetColor(color);
|
|
PRUint8 style;
|
|
style = borderStyle->GetBorderStyle(NS_SIDE_LEFT);
|
|
aRenderingContext.SetLineStyle(ConvertBorderStyleToLineStyle(style));
|
|
|
|
nsRect imageSize(0,0,0,0);
|
|
|
|
PRInt32 x = currX;
|
|
PRInt32 y = (aRowIndex - mTopRowIndex) * mRowHeight;
|
|
|
|
// Compute the maximal level to paint.
|
|
PRInt32 maxLevel = level;
|
|
if (maxLevel > cellRect.width / mIndentation)
|
|
maxLevel = cellRect.width / mIndentation;
|
|
|
|
PRInt32 currentParent = aRowIndex;
|
|
for (PRInt32 i = level; i > 0; i--) {
|
|
if (i <= maxLevel) {
|
|
// Get size of parent image to line up.
|
|
PrefillPropertyArray(currentParent, aColumn);
|
|
mView->GetCellProperties(currentParent, aColumn->GetID().get(), mScratchArray);
|
|
|
|
nsCOMPtr<nsIStyleContext> imageContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinerimage, getter_AddRefs(imageContext));
|
|
|
|
imageSize = GetImageSize(currentParent, aColumn->GetID().get(), imageContext);
|
|
|
|
const nsStyleMargin* imageMarginData = (const nsStyleMargin*)imageContext->GetStyleData(eStyleStruct_Margin);
|
|
nsMargin imageMargin;
|
|
imageMarginData->GetMargin(imageMargin);
|
|
imageSize.Inflate(imageMargin);
|
|
|
|
// Use default indentation if no parent image
|
|
if (!imageSize.width)
|
|
imageSize.width = mIndentation;
|
|
|
|
// Line up line with the parent image.
|
|
x = currX + twistySize.width + imageSize.width / 2;
|
|
|
|
// Paint full vertical line only if we have next sibling.
|
|
PRBool hasNextSibling;
|
|
mView->HasNextSibling(currentParent, aRowIndex, &hasNextSibling);
|
|
if (hasNextSibling)
|
|
aRenderingContext.DrawLine(x - (level - i + 1) * mIndentation, y, x - (level - i + 1) * mIndentation, y + mRowHeight);
|
|
else if (i == level)
|
|
aRenderingContext.DrawLine(x - (level - i + 1) * mIndentation, y, x - (level - i + 1) * mIndentation, y + mRowHeight / 2);
|
|
}
|
|
|
|
PRInt32 parent;
|
|
mView->GetParentIndex(currentParent, &parent);
|
|
if (parent == -1)
|
|
break;
|
|
currentParent = parent;
|
|
}
|
|
|
|
// Don't paint off our cell.
|
|
if (level == maxLevel)
|
|
aRenderingContext.DrawLine(x - mIndentation + 16, y + mRowHeight / 2, x - imageSize.width / 2, y + mRowHeight / 2);
|
|
|
|
PRBool clipState;
|
|
aRenderingContext.PopState(clipState);
|
|
|
|
PrefillPropertyArray(aRowIndex, aColumn);
|
|
mView->GetCellProperties(aRowIndex, aColumn->GetID().get(), mScratchArray);
|
|
}
|
|
|
|
// Always leave space for the twisty.
|
|
nsRect twistyRect(currX, cellRect.y, remainingWidth, cellRect.height);
|
|
PaintTwisty(aRowIndex, aColumn, twistyRect, aPresContext, aRenderingContext, aDirtyRect, aWhichLayer,
|
|
remainingWidth, currX);
|
|
}
|
|
|
|
// Now paint the icon for our cell.
|
|
nsRect iconRect(currX, cellRect.y, remainingWidth, cellRect.height);
|
|
nsRect dirtyRect;
|
|
if (dirtyRect.IntersectRect(aDirtyRect, iconRect))
|
|
PaintImage(aRowIndex, aColumn, iconRect, aPresContext, aRenderingContext, aDirtyRect, aWhichLayer,
|
|
remainingWidth, currX);
|
|
|
|
// Now paint our text, but only if we aren't a cycler column.
|
|
// XXX until we have the ability to load images, allow the view to
|
|
// insert text into cycler columns...
|
|
if (!aColumn->IsCycler()) {
|
|
nsRect textRect(currX, cellRect.y, remainingWidth, cellRect.height);
|
|
nsRect dirtyRect;
|
|
if (dirtyRect.IntersectRect(aDirtyRect, textRect))
|
|
PaintText(aRowIndex, aColumn, textRect, aPresContext, aRenderingContext, aDirtyRect, aWhichLayer);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame::PaintTwisty(int aRowIndex,
|
|
nsOutlinerColumn* aColumn,
|
|
const nsRect& aTwistyRect,
|
|
nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nsFramePaintLayer aWhichLayer,
|
|
nscoord& aRemainingWidth,
|
|
nscoord& aCurrX)
|
|
{
|
|
// Paint the twisty, but only if we are a non-empty container.
|
|
PRBool shouldPaint = PR_FALSE;
|
|
PRBool isContainer = PR_FALSE;
|
|
mView->IsContainer(aRowIndex, &isContainer);
|
|
if (isContainer) {
|
|
PRBool isContainerEmpty = PR_FALSE;
|
|
mView->IsContainerEmpty(aRowIndex, &isContainerEmpty);
|
|
if (!isContainerEmpty)
|
|
shouldPaint = PR_TRUE;
|
|
}
|
|
|
|
// Resolve style for the twisty.
|
|
nsCOMPtr<nsIStyleContext> twistyContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinertwisty, getter_AddRefs(twistyContext));
|
|
|
|
PRBool useTheme = PR_FALSE;
|
|
nsCOMPtr<nsITheme> theme;
|
|
const nsStyleDisplay* twistyDisplayData = (const nsStyleDisplay*)twistyContext->GetStyleData(eStyleStruct_Display);
|
|
if ( twistyDisplayData->mAppearance ) {
|
|
aPresContext->GetTheme(getter_AddRefs(theme));
|
|
if (theme && theme->ThemeSupportsWidget(aPresContext, twistyDisplayData->mAppearance))
|
|
useTheme = PR_TRUE;
|
|
}
|
|
|
|
// Obtain the margins for the twisty and then deflate our rect by that
|
|
// amount. The twisty is assumed to be contained within the deflated rect.
|
|
nsRect twistyRect(aTwistyRect);
|
|
const nsStyleMargin* twistyMarginData = (const nsStyleMargin*)twistyContext->GetStyleData(eStyleStruct_Margin);
|
|
nsMargin twistyMargin;
|
|
twistyMarginData->GetMargin(twistyMargin);
|
|
twistyRect.Deflate(twistyMargin);
|
|
|
|
// The twisty rect extends all the way to the end of the cell. This is incorrect. We need to
|
|
// determine the twisty rect's true width. This is done by examining the style context for
|
|
// a width first. If it has one, we use that. If it doesn't, we use the image's natural width.
|
|
// If the image hasn't loaded and if no width is specified, then we just bail. If there is
|
|
// a -moz-apperance involved, adjust the rect by the minimum widget size provided by
|
|
// the theme implementation.
|
|
nsRect imageSize = GetImageSize(aRowIndex, aColumn->GetID().get(), twistyContext);
|
|
twistyRect.width = imageSize.width;
|
|
if ( useTheme ) {
|
|
nsSize minTwistySize(0,0);
|
|
PRBool canOverride = PR_TRUE;
|
|
theme->GetMinimumWidgetSize(&aRenderingContext, this, twistyDisplayData->mAppearance, &minTwistySize, &canOverride);
|
|
|
|
// GMWS() returns size in pixels, we need to convert it back to twips
|
|
float p2t;
|
|
aPresContext->GetScaledPixelsToTwips(&p2t);
|
|
minTwistySize.width = NSIntPixelsToTwips(minTwistySize.width, p2t);
|
|
minTwistySize.height = NSIntPixelsToTwips(minTwistySize.height, p2t);
|
|
|
|
if ( twistyRect.width < minTwistySize.width || !canOverride )
|
|
twistyRect.width = minTwistySize.width;
|
|
}
|
|
|
|
// Subtract out the remaining width. This is done even when we don't actually paint a twisty in
|
|
// this cell, so that cells in different rows still line up.
|
|
nsRect copyRect(twistyRect);
|
|
copyRect.Inflate(twistyMargin);
|
|
aRemainingWidth -= copyRect.width;
|
|
aCurrX += copyRect.width;
|
|
|
|
if (shouldPaint) {
|
|
// If the layer is the background layer, we must paint our borders and background for our
|
|
// image rect.
|
|
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer)
|
|
PaintBackgroundLayer(twistyContext, aPresContext, aRenderingContext, twistyRect, aDirtyRect);
|
|
else if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
|
|
if ( useTheme ) {
|
|
// yeah, i know it says we're drawing a background, but a twisty is really a fg
|
|
// object since it doesn't have anything that gecko would want to draw over it. Besides,
|
|
// we have to prevent imagelib from drawing it.
|
|
theme->DrawWidgetBackground(&aRenderingContext, this,
|
|
twistyDisplayData->mAppearance, twistyRect, aDirtyRect);
|
|
}
|
|
else {
|
|
// Time to paint the twisty.
|
|
// Adjust the rect for its border and padding.
|
|
AdjustForBorderPadding(twistyContext, twistyRect);
|
|
AdjustForBorderPadding(twistyContext, imageSize);
|
|
|
|
// Get the image for drawing.
|
|
nsCOMPtr<imgIContainer> image;
|
|
GetImage(aRowIndex, aColumn->GetID().get(), twistyContext, getter_AddRefs(image));
|
|
if (image) {
|
|
nsPoint p(twistyRect.x, twistyRect.y);
|
|
|
|
// Center the image. XXX Obey vertical-align style prop?
|
|
if (imageSize.height < twistyRect.height) {
|
|
p.y += (twistyRect.height - imageSize.height)/2;
|
|
float t2p;
|
|
mPresContext->GetTwipsToPixels(&t2p);
|
|
if (NSTwipsToIntPixels(twistyRect.height - imageSize.height, t2p)%2 != 0) {
|
|
float p2t;
|
|
mPresContext->GetPixelsToTwips(&p2t);
|
|
p.y -= NSIntPixelsToTwips(1, p2t);
|
|
}
|
|
}
|
|
|
|
// Paint the image.
|
|
aRenderingContext.DrawImage(image, &imageSize, &p);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame::PaintImage(int aRowIndex,
|
|
nsOutlinerColumn* aColumn,
|
|
const nsRect& aImageRect,
|
|
nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nsFramePaintLayer aWhichLayer,
|
|
nscoord& aRemainingWidth,
|
|
nscoord& aCurrX)
|
|
{
|
|
// Resolve style for the image.
|
|
nsCOMPtr<nsIStyleContext> imageContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinerimage, getter_AddRefs(imageContext));
|
|
|
|
// Obtain the margins for the twisty and then deflate our rect by that
|
|
// amount. The twisty is assumed to be contained within the deflated rect.
|
|
nsRect imageRect(aImageRect);
|
|
const nsStyleMargin* imageMarginData = (const nsStyleMargin*)imageContext->GetStyleData(eStyleStruct_Margin);
|
|
nsMargin imageMargin;
|
|
imageMarginData->GetMargin(imageMargin);
|
|
imageRect.Deflate(imageMargin);
|
|
|
|
// If the column isn't a cycler, the image rect extends all the way to the end of the cell.
|
|
// This is incorrect. We need to determine the image rect's true width. This is done by
|
|
// examining the style context for a width first. If it has one, we use that. If it doesn't,
|
|
// we use the image's natural width.
|
|
// If the image hasn't loaded and if no width is specified, then we just bail.
|
|
nsRect imageSize = GetImageSize(aRowIndex, aColumn->GetID().get(), imageContext);
|
|
if (!aColumn->IsCycler())
|
|
imageRect.width = imageSize.width;
|
|
|
|
// Subtract out the remaining width.
|
|
nsRect copyRect(imageRect);
|
|
copyRect.Inflate(imageMargin);
|
|
aRemainingWidth -= copyRect.width;
|
|
aCurrX += copyRect.width;
|
|
|
|
// If the layer is the background layer, we must paint our borders and background for our
|
|
// image rect.
|
|
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer)
|
|
PaintBackgroundLayer(imageContext, aPresContext, aRenderingContext, imageRect, aDirtyRect);
|
|
else if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
|
|
// Time to paint the twisty.
|
|
// Adjust the rect for its border and padding.
|
|
AdjustForBorderPadding(imageContext, imageRect);
|
|
AdjustForBorderPadding(imageContext, imageSize);
|
|
|
|
#ifdef USE_IMG2
|
|
// Get the image for drawing.
|
|
nsCOMPtr<imgIContainer> image;
|
|
GetImage(aRowIndex, aColumn->GetID().get(), imageContext, getter_AddRefs(image));
|
|
if (image) {
|
|
nsPoint p(imageRect.x, imageRect.y);
|
|
|
|
// Center the image. XXX Obey vertical-align style prop?
|
|
|
|
float t2p, p2t;
|
|
mPresContext->GetTwipsToPixels(&t2p);
|
|
mPresContext->GetPixelsToTwips(&p2t);
|
|
|
|
if (imageSize.height < imageRect.height) {
|
|
p.y += (imageRect.height - imageSize.height)/2;
|
|
if (NSTwipsToIntPixels(imageRect.height - imageSize.height, t2p)%2 != 0)
|
|
p.y -= NSIntPixelsToTwips(1, p2t); // One pixel in twips
|
|
}
|
|
|
|
// For cyclers, we also want to center the image in the column.
|
|
if (aColumn->IsCycler() && imageSize.width < imageRect.width) {
|
|
p.x += (imageRect.width - imageSize.width)/2;
|
|
if (NSTwipsToIntPixels(imageRect.width - imageSize.width, t2p)%2 != 0)
|
|
p.x -= NSIntPixelsToTwips(1, p2t); // One pixel in twips
|
|
}
|
|
|
|
// Paint the image.
|
|
aRenderingContext.DrawImage(image, &imageSize, &p);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::PaintText(int aRowIndex,
|
|
nsOutlinerColumn* aColumn,
|
|
const nsRect& aTextRect,
|
|
nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nsFramePaintLayer aWhichLayer)
|
|
{
|
|
// Now obtain the text for our cell.
|
|
nsAutoString text;
|
|
mView->GetCellText(aRowIndex, aColumn->GetID().get(), text);
|
|
|
|
if (text.Length() == 0)
|
|
return NS_OK; // Don't paint an empty string. XXX What about background/borders? Still paint?
|
|
|
|
// Resolve style for the text. It contains all the info we need to lay ourselves
|
|
// out and to paint.
|
|
nsCOMPtr<nsIStyleContext> textContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinercelltext, getter_AddRefs(textContext));
|
|
|
|
// Obtain the margins for the text and then deflate our rect by that
|
|
// amount. The text is assumed to be contained within the deflated rect.
|
|
nsRect textRect(aTextRect);
|
|
const nsStyleMargin* textMarginData = (const nsStyleMargin*)textContext->GetStyleData(eStyleStruct_Margin);
|
|
nsMargin textMargin;
|
|
textMarginData->GetMargin(textMargin);
|
|
textRect.Deflate(textMargin);
|
|
|
|
// Compute our text size.
|
|
const nsStyleFont* fontStyle = (const nsStyleFont*)textContext->GetStyleData(eStyleStruct_Font);
|
|
|
|
nsCOMPtr<nsIDeviceContext> deviceContext;
|
|
aPresContext->GetDeviceContext(getter_AddRefs(deviceContext));
|
|
|
|
nsCOMPtr<nsIFontMetrics> fontMet;
|
|
deviceContext->GetMetricsFor(fontStyle->mFont, *getter_AddRefs(fontMet));
|
|
nscoord height, baseline;
|
|
fontMet->GetHeight(height);
|
|
fontMet->GetMaxAscent(baseline);
|
|
|
|
// Center the text. XXX Obey vertical-align style prop?
|
|
if (height < textRect.height) {
|
|
textRect.y += (textRect.height - height)/2;
|
|
textRect.height = height;
|
|
}
|
|
|
|
// Set our font.
|
|
aRenderingContext.SetFont(fontMet);
|
|
|
|
nscoord width;
|
|
aRenderingContext.GetWidth(text, width);
|
|
|
|
if (width > textRect.width) {
|
|
// See if the width is even smaller than the ellipsis
|
|
// If so, clear the text completely.
|
|
nscoord ellipsisWidth;
|
|
aRenderingContext.GetWidth(ELLIPSIS, ellipsisWidth);
|
|
|
|
nscoord width = textRect.width;
|
|
if (ellipsisWidth > width)
|
|
text.SetLength(0);
|
|
else if (ellipsisWidth == width)
|
|
text.Assign(NS_LITERAL_STRING(ELLIPSIS));
|
|
else {
|
|
// We will be drawing an ellipsis, thank you very much.
|
|
// Subtract out the required width of the ellipsis.
|
|
// This is the total remaining width we have to play with.
|
|
width -= ellipsisWidth;
|
|
|
|
// Now we crop.
|
|
switch (aColumn->GetCropStyle()) {
|
|
default:
|
|
case 0: {
|
|
// Crop right.
|
|
nscoord cwidth;
|
|
nscoord twidth = 0;
|
|
int length = text.Length();
|
|
int i;
|
|
for (i = 0; i < length; ++i) {
|
|
PRUnichar ch = text[i];
|
|
aRenderingContext.GetWidth(ch,cwidth);
|
|
if (twidth + cwidth > width)
|
|
break;
|
|
twidth += cwidth;
|
|
}
|
|
text.Truncate(i);
|
|
text += NS_LITERAL_STRING(ELLIPSIS);
|
|
}
|
|
break;
|
|
|
|
case 2: {
|
|
// Crop left.
|
|
nscoord cwidth;
|
|
nscoord twidth = 0;
|
|
int length = text.Length();
|
|
int i;
|
|
for (i=length-1; i >= 0; --i) {
|
|
PRUnichar ch = text[i];
|
|
aRenderingContext.GetWidth(ch,cwidth);
|
|
if (twidth + cwidth > width)
|
|
break;
|
|
twidth += cwidth;
|
|
}
|
|
|
|
nsAutoString copy;
|
|
text.Right(copy, length-1-i);
|
|
text.Assign(NS_LITERAL_STRING(ELLIPSIS));
|
|
text += copy;
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
{
|
|
// Crop center.
|
|
nsAutoString leftStr, rightStr;
|
|
nscoord cwidth, twidth = 0;
|
|
int length = text.Length();
|
|
int rightPos = length - 1;
|
|
for (int leftPos = 0; leftPos < rightPos; ++leftPos) {
|
|
PRUnichar ch = text[leftPos];
|
|
aRenderingContext.GetWidth(ch, cwidth);
|
|
twidth += cwidth;
|
|
if (twidth > width)
|
|
break;
|
|
leftStr.Append(ch);
|
|
|
|
ch = text[rightPos];
|
|
aRenderingContext.GetWidth(ch, cwidth);
|
|
twidth += cwidth;
|
|
if (twidth > width)
|
|
break;
|
|
rightStr.Insert(ch, 0);
|
|
--rightPos;
|
|
}
|
|
text = leftStr + NS_LITERAL_STRING(ELLIPSIS) + rightStr;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
switch (aColumn->GetTextAlignment()) {
|
|
case NS_STYLE_TEXT_ALIGN_RIGHT: {
|
|
textRect.x += textRect.width - width;
|
|
}
|
|
break;
|
|
case NS_STYLE_TEXT_ALIGN_CENTER: {
|
|
textRect.x += (textRect.width - width) / 2;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
aRenderingContext.GetWidth(text, width);
|
|
textRect.width = width;
|
|
|
|
// If the layer is the background layer, we must paint our borders and background for our
|
|
// text rect.
|
|
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer)
|
|
PaintBackgroundLayer(textContext, aPresContext, aRenderingContext, textRect, aDirtyRect);
|
|
else if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
|
|
// Time to paint our text.
|
|
// Adjust the rect for its border and padding.
|
|
AdjustForBorderPadding(textContext, textRect);
|
|
|
|
// Set our color.
|
|
const nsStyleColor* colorStyle = (const nsStyleColor*)textContext->GetStyleData(eStyleStruct_Color);
|
|
aRenderingContext.SetColor(colorStyle->mColor);
|
|
|
|
// Draw decorations.
|
|
const nsStyleTextReset* textStyle = (const nsStyleTextReset*)textContext->GetStyleData(eStyleStruct_TextReset);
|
|
PRUint8 decorations = textStyle->mTextDecoration;
|
|
|
|
nscoord offset;
|
|
nscoord size;
|
|
if (decorations & (NS_FONT_DECORATION_OVERLINE | NS_FONT_DECORATION_UNDERLINE)) {
|
|
fontMet->GetUnderline(offset, size);
|
|
if (decorations & NS_FONT_DECORATION_OVERLINE)
|
|
aRenderingContext.FillRect(textRect.x, textRect.y, width, size);
|
|
if (decorations & NS_FONT_DECORATION_UNDERLINE)
|
|
aRenderingContext.FillRect(textRect.x, textRect.y + baseline - offset, width, size);
|
|
}
|
|
if (decorations & NS_FONT_DECORATION_LINE_THROUGH) {
|
|
fontMet->GetStrikeout(offset, size);
|
|
aRenderingContext.FillRect(textRect.x, textRect.y + baseline - offset, width, size);
|
|
}
|
|
|
|
aRenderingContext.DrawString(text, textRect.x, textRect.y + baseline);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame::PaintBackgroundLayer(nsIStyleContext* aStyleContext, nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aRect, const nsRect& aDirtyRect)
|
|
{
|
|
|
|
const nsStyleBackground* myColor = (const nsStyleBackground*)
|
|
aStyleContext->GetStyleData(eStyleStruct_Background);
|
|
const nsStyleBorder* myBorder = (const nsStyleBorder*)
|
|
aStyleContext->GetStyleData(eStyleStruct_Border);
|
|
const nsStyleOutline* myOutline = (const nsStyleOutline*)
|
|
aStyleContext->GetStyleData(eStyleStruct_Outline);
|
|
|
|
nsCSSRendering::PaintBackgroundWithSC(aPresContext, aRenderingContext,
|
|
this, aDirtyRect, aRect,
|
|
*myColor, *myBorder, 0, 0);
|
|
|
|
nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this,
|
|
aDirtyRect, aRect, *myBorder, mStyleContext, 0);
|
|
|
|
nsCSSRendering::PaintOutline(aPresContext, aRenderingContext, this,
|
|
aDirtyRect, aRect, *myBorder, *myOutline,
|
|
aStyleContext, 0);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// Scrolling
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::EnsureRowIsVisible(PRInt32 aRow)
|
|
{
|
|
if (!mView)
|
|
return NS_OK;
|
|
|
|
if (mTopRowIndex <= aRow && mTopRowIndex+mPageCount > aRow)
|
|
return NS_OK;
|
|
|
|
if (aRow < mTopRowIndex)
|
|
ScrollToRow(aRow);
|
|
else {
|
|
// Bring it just on-screen.
|
|
PRInt32 distance = aRow - (mTopRowIndex+mPageCount)+1;
|
|
ScrollToRow(mTopRowIndex+distance);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::ScrollToRow(PRInt32 aRow)
|
|
{
|
|
ScrollInternal(aRow);
|
|
UpdateScrollbar();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::ScrollByLines(PRInt32 aNumLines)
|
|
{
|
|
if (!mView)
|
|
return NS_OK;
|
|
|
|
PRInt32 newIndex = mTopRowIndex + aNumLines;
|
|
if (newIndex < 0)
|
|
newIndex = 0;
|
|
else {
|
|
PRInt32 rowCount;
|
|
mView->GetRowCount(&rowCount);
|
|
PRInt32 lastPageTopRow = rowCount - mPageCount;
|
|
if (newIndex > lastPageTopRow)
|
|
newIndex = lastPageTopRow;
|
|
}
|
|
ScrollToRow(newIndex);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::ScrollByPages(PRInt32 aNumPages)
|
|
{
|
|
if (!mView)
|
|
return NS_OK;
|
|
|
|
PRInt32 newIndex = mTopRowIndex + aNumPages * mPageCount;
|
|
if (newIndex < 0)
|
|
newIndex = 0;
|
|
else {
|
|
PRInt32 rowCount;
|
|
mView->GetRowCount(&rowCount);
|
|
PRInt32 lastPageTopRow = rowCount - mPageCount;
|
|
if (newIndex > lastPageTopRow)
|
|
newIndex = lastPageTopRow;
|
|
}
|
|
ScrollToRow(newIndex);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsOutlinerBodyFrame::ScrollInternal(PRInt32 aRow)
|
|
{
|
|
if (!mView)
|
|
return NS_OK;
|
|
|
|
PRInt32 rowCount;
|
|
mView->GetRowCount(&rowCount);
|
|
|
|
PRInt32 delta = aRow - mTopRowIndex;
|
|
|
|
if (delta > 0) {
|
|
if (mTopRowIndex == (rowCount - mPageCount + 1))
|
|
return NS_OK;
|
|
}
|
|
else {
|
|
if (mTopRowIndex == 0)
|
|
return NS_OK;
|
|
}
|
|
|
|
mTopRowIndex += delta;
|
|
|
|
float t2p;
|
|
mPresContext->GetTwipsToPixels(&t2p);
|
|
nscoord rowHeightAsPixels = NSToCoordRound((float)mRowHeight*t2p);
|
|
|
|
// See if we have a background image. If we do, then we cannot blit.
|
|
const nsStyleBackground* myColor = (const nsStyleBackground*)
|
|
mStyleContext->GetStyleData(eStyleStruct_Background);
|
|
PRBool hasBackground = myColor->mBackgroundImage.Length() > 0;
|
|
|
|
PRInt32 absDelta = delta > 0 ? delta : -delta;
|
|
if (hasBackground || absDelta*mRowHeight >= mRect.height)
|
|
Invalidate();
|
|
else if (mOutlinerWidget) {
|
|
mOutlinerWidget->Scroll(0, -delta*rowHeightAsPixels, nsnull);
|
|
#if defined(XP_MAC) || defined(XP_MACOSX)
|
|
// mac can't process the event loop during a drag, so if we're dragging,
|
|
// update outliner widget synchronously.
|
|
if (mDragSession)
|
|
mOutlinerWidget->Update();
|
|
#endif
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame::ScrollbarButtonPressed(PRInt32 aOldIndex, PRInt32 aNewIndex)
|
|
{
|
|
if (aNewIndex > aOldIndex)
|
|
ScrollToRow(mTopRowIndex+1);
|
|
else if (aNewIndex < aOldIndex)
|
|
ScrollToRow(mTopRowIndex-1);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame::PositionChanged(PRInt32 aOldIndex, PRInt32& aNewIndex)
|
|
{
|
|
float t2p;
|
|
if (!mRowHeight) return NS_ERROR_UNEXPECTED;
|
|
mPresContext->GetTwipsToPixels(&t2p);
|
|
nscoord rh = NSToCoordRound((float)mRowHeight*t2p);
|
|
|
|
nscoord oldrow = aOldIndex/rh;
|
|
nscoord newrow = aNewIndex/rh;
|
|
|
|
if (oldrow != newrow)
|
|
ScrollInternal(newrow);
|
|
|
|
// Go exactly where we're supposed to
|
|
// Update the scrollbar.
|
|
nsCOMPtr<nsIContent> scrollbarContent;
|
|
mScrollbar->GetContent(getter_AddRefs(scrollbarContent));
|
|
nsAutoString curPos;
|
|
curPos.AppendInt(aNewIndex);
|
|
scrollbarContent->SetAttr(kNameSpaceID_None, nsXULAtoms::curpos, curPos, PR_TRUE);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// The style cache.
|
|
nsresult
|
|
nsOutlinerBodyFrame::GetPseudoStyleContext(nsIAtom* aPseudoElement,
|
|
nsIStyleContext** aResult)
|
|
{
|
|
return mStyleCache.GetStyleContext(this, mPresContext, mContent, mStyleContext, aPseudoElement,
|
|
mScratchArray, aResult);
|
|
}
|
|
|
|
// Our comparator for resolving our complex pseudos
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame::PseudoMatches(nsIAtom* aTag, nsCSSSelector* aSelector, PRBool* aResult)
|
|
{
|
|
if (aSelector->mTag == aTag) {
|
|
// Iterate the pseudoclass list. For each item in the list, see if
|
|
// it is contained in our scratch array. If we have a miss, then
|
|
// we aren't a match. If all items in the pseudoclass list are
|
|
// present in the scratch array, then we have a match.
|
|
nsAtomList* curr = aSelector->mPseudoClassList;
|
|
while (curr) {
|
|
PRInt32 index;
|
|
mScratchArray->GetIndexOf(curr->mAtom, &index);
|
|
if (index == -1) {
|
|
*aResult = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
curr = curr->mNext;
|
|
}
|
|
*aResult = PR_TRUE;
|
|
}
|
|
else
|
|
*aResult = PR_FALSE;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsOutlinerBodyFrame::InvalidateColumnCache()
|
|
{
|
|
mColumnsDirty = PR_TRUE;
|
|
}
|
|
|
|
void
|
|
nsOutlinerBodyFrame::EnsureColumns()
|
|
{
|
|
if (!mColumns || mColumnsDirty) {
|
|
delete mColumns;
|
|
mColumnsDirty = PR_FALSE;
|
|
|
|
nsCOMPtr<nsIContent> parent;
|
|
GetBaseElement(getter_AddRefs(parent));
|
|
|
|
if (!parent)
|
|
return;
|
|
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
mPresContext->GetShell(getter_AddRefs(shell));
|
|
|
|
// Note: this is dependent on the anonymous content for select
|
|
// defined in select.xml
|
|
nsCOMPtr<nsIAtom> parentTag;
|
|
parent->GetTag(*getter_AddRefs(parentTag));
|
|
if (parentTag == nsHTMLAtoms::select) {
|
|
// We can avoid crawling the content nodes in this case, since we know
|
|
// that we have a single column, and we know where it's at.
|
|
|
|
ChildIterator iter, last;
|
|
ChildIterator::Init(parent, &iter, &last);
|
|
nsCOMPtr<nsIContent> outlinerCols = *iter;
|
|
nsCOMPtr<nsIContent> column;
|
|
outlinerCols->ChildAt(0, *getter_AddRefs(column));
|
|
|
|
nsIFrame* colFrame = nsnull;
|
|
shell->GetPrimaryFrameFor(column, &colFrame);
|
|
mColumns = new nsOutlinerColumn(column, colFrame);
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIContent> colsContent;
|
|
nsOutlinerUtils::GetImmediateChild(parent, nsXULAtoms::outlinercols, getter_AddRefs(colsContent));
|
|
if (!colsContent)
|
|
return;
|
|
|
|
nsIFrame* colsFrame = nsnull;
|
|
shell->GetPrimaryFrameFor(colsContent, &colsFrame);
|
|
if (!colsFrame)
|
|
return;
|
|
|
|
nsCOMPtr<nsIBox> colsBox(do_QueryInterface(colsFrame));
|
|
nsIBox* colBox = nsnull;
|
|
colsBox->GetChildBox(&colBox);
|
|
nsOutlinerColumn* currCol = nsnull;
|
|
while (colBox) {
|
|
nsIFrame* frame = nsnull;
|
|
colBox->GetFrame(&frame);
|
|
nsCOMPtr<nsIContent> content;
|
|
frame->GetContent(getter_AddRefs(content));
|
|
nsCOMPtr<nsIAtom> tag;
|
|
content->GetTag(*getter_AddRefs(tag));
|
|
if (tag == nsXULAtoms::outlinercol) {
|
|
// Create a new column structure.
|
|
nsOutlinerColumn* col = new nsOutlinerColumn(content, frame);
|
|
if (currCol)
|
|
currCol->SetNext(col);
|
|
else mColumns = col;
|
|
currCol = col;
|
|
}
|
|
|
|
colBox->GetNextBox(&colBox);
|
|
}
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
nsOutlinerBodyFrame::GetBaseElement(nsIContent** aContent)
|
|
{
|
|
nsCOMPtr<nsIContent> parent = mContent;
|
|
nsCOMPtr<nsIAtom> tag;
|
|
nsCOMPtr<nsIContent> temp;
|
|
|
|
while (parent && NS_SUCCEEDED(parent->GetTag(*getter_AddRefs(tag)))
|
|
&& tag != nsXULAtoms::outliner && tag != nsHTMLAtoms::select) {
|
|
temp = parent;
|
|
temp->GetParent(*getter_AddRefs(parent));
|
|
}
|
|
|
|
*aContent = parent;
|
|
NS_IF_ADDREF(*aContent);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::ClearStyleAndImageCaches()
|
|
{
|
|
mStyleCache.Clear();
|
|
mImageCache = nsnull;
|
|
mScrollbar = nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
#ifdef XP_MAC
|
|
#pragma mark -
|
|
#endif
|
|
|
|
// Tell the view where the drop happened
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame::OnDragDrop (nsIDOMEvent* aEvent)
|
|
{
|
|
mView->Drop (mDropRow, mDropOrient);
|
|
return NS_OK;
|
|
} // OnDragDrop
|
|
|
|
// Clear out all our tracking vars.
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame::OnDragExit(nsIDOMEvent* aEvent)
|
|
{
|
|
if (mDropAllowed) {
|
|
mDropAllowed = PR_FALSE;
|
|
InvalidatePrimaryCell(mDropRow);
|
|
}
|
|
else
|
|
mDropAllowed = PR_FALSE;
|
|
mDropRow = -1;
|
|
mDropOrient = -1;
|
|
mDragSession = nsnull;
|
|
|
|
if (mOpenTimer) {
|
|
mOpenTimer->Cancel();
|
|
mOpenTimer = nsnull;
|
|
}
|
|
|
|
return NS_OK;
|
|
} // OnDragExit
|
|
|
|
// The mouse is hovering over this outliner. If we determine things are different from the
|
|
// last time, invalidate primary cell at the old position, query the view to see if the current location is
|
|
// droppable, and then invalidate primary cell at the new location if it is. The mouse may or may
|
|
// not have changed position from the last time we were called, so optimize out a lot of
|
|
// the extra notifications by checking if anything changed first.
|
|
// For drop feedback we use drop, dropBefore and dropAfter property.
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame::OnDragOver(nsIDOMEvent* aEvent)
|
|
{
|
|
// while we're here, handle tracking of scrolling during a drag.
|
|
PRBool scrollUp = PR_FALSE;
|
|
if (IsInDragScrollRegion(aEvent, &scrollUp)) {
|
|
if (mDropAllowed) {
|
|
// invalidate primary cell at old location.
|
|
mDropAllowed = PR_FALSE;
|
|
InvalidatePrimaryCell(mDropRow);
|
|
}
|
|
ScrollByLines(scrollUp ? -1 : 1);
|
|
return NS_OK;
|
|
}
|
|
|
|
// compute the row mouse is over and the above/below/on state. Below we'll use this
|
|
// to see if anything changed.
|
|
PRInt32 newRow = -1;
|
|
PRInt16 newOrient = -1;
|
|
ComputeDropPosition(aEvent, &newRow, &newOrient);
|
|
|
|
// if changed from last time, invalidate primary cell at the old location and if allowed,
|
|
// invalidate primary cell at the new location. If nothing changed, just bail.
|
|
if (newRow != mDropRow || newOrient != mDropOrient) {
|
|
// Invalidate row at the old location.
|
|
if (mDropAllowed) {
|
|
mDropAllowed = PR_FALSE;
|
|
InvalidatePrimaryCell(mDropRow);
|
|
}
|
|
|
|
if (mOpenTimer) {
|
|
// timer is active but for a different row than the current one - kill it
|
|
mOpenTimer->Cancel();
|
|
mOpenTimer = nsnull;
|
|
}
|
|
|
|
// cache the new row and orientation regardless so we can check if it changed
|
|
// for next time.
|
|
mDropRow = newRow;
|
|
mDropOrient = newOrient;
|
|
mDropAllowed = PR_FALSE;
|
|
|
|
if (mDropRow >= 0) {
|
|
if (!mOpenTimer && mDropOrient == nsIOutlinerView::inDropOn) {
|
|
// either there wasn't a timer running or it was just killed above.
|
|
// if over a folder, start up a timer to open the folder.
|
|
PRBool isContainer = PR_FALSE;
|
|
mView->IsContainer(mDropRow, &isContainer);
|
|
if (isContainer) {
|
|
PRBool isOpen = PR_FALSE;
|
|
mView->IsContainerOpen(mDropRow, &isOpen);
|
|
if (!isOpen) {
|
|
// this node isn't expanded - set a timer to expand it
|
|
mOpenTimer = do_CreateInstance("@mozilla.org/timer;1");
|
|
mOpenTimer->Init(this, 1000, NS_PRIORITY_HIGHEST);
|
|
}
|
|
}
|
|
}
|
|
|
|
PRBool canDropAtNewLocation = PR_FALSE;
|
|
if (mDropOrient == nsIOutlinerView::inDropOn)
|
|
mView->CanDropOn(mDropRow, &canDropAtNewLocation);
|
|
else
|
|
mView->CanDropBeforeAfter (mDropRow, mDropOrient == nsIOutlinerView::inDropBefore ? PR_TRUE : PR_FALSE, &canDropAtNewLocation);
|
|
|
|
if (canDropAtNewLocation) {
|
|
// Invalidate row at the new location/
|
|
mDropAllowed = canDropAtNewLocation;
|
|
InvalidatePrimaryCell(mDropRow);
|
|
}
|
|
}
|
|
}
|
|
|
|
// alert the drag session we accept the drop. We have to do this every time
|
|
// since the |canDrop| attribute is reset before we're called.
|
|
if ( mDropAllowed && mDragSession )
|
|
mDragSession->SetCanDrop(PR_TRUE);
|
|
|
|
return NS_OK;
|
|
} // OnDragOver
|
|
|
|
// Given a dom event, figure out which row in the tree the mouse is over
|
|
// and if we should drop before/after/on that row. Doesn't query the content
|
|
// about if the drag is allowable, that's done elsewhere.
|
|
//
|
|
// For containers, we break up the vertical space of the row as follows: if in
|
|
// the topmost 25%, the drop is _before_ the row the mouse is over; if in the
|
|
// last 25%, _after_; in the middle 50%, we consider it a drop _on_ the container.
|
|
//
|
|
// For non-containers, if the mouse is in the top 50% of the row, the drop is
|
|
// _before_ and the bottom 50% _after_
|
|
void
|
|
nsOutlinerBodyFrame::ComputeDropPosition(nsIDOMEvent* aEvent, PRInt32* aRow, PRInt16* aOrient)
|
|
{
|
|
nsCOMPtr<nsIDOMMouseEvent> mouseEvent (do_QueryInterface(aEvent));
|
|
if (mouseEvent) {
|
|
PRInt32 x = 0, y = 0;
|
|
mouseEvent->GetClientX(&x); mouseEvent->GetClientY(&y);
|
|
|
|
nsXPIDLString colID, child;
|
|
GetCellAt (x, y, aRow, getter_Copies(colID), getter_Copies(child));
|
|
if (*aRow == -1) {
|
|
*aOrient = -1;
|
|
return;
|
|
}
|
|
|
|
// Compute the top/bottom of the row in question. We need to convert
|
|
// our y coord to twips since |mRowHeight| is in twips.
|
|
PRInt32 yTwips, xTwips;
|
|
AdjustEventCoordsToBoxCoordSpace (x, y, &xTwips, &yTwips);
|
|
PRInt32 yOffset = yTwips - mRowHeight * (*aRow - mTopRowIndex);
|
|
|
|
PRBool isContainer = PR_FALSE;
|
|
mView->IsContainer (*aRow, &isContainer);
|
|
if (isContainer) {
|
|
// for a container, use a 25%/50%/25% breakdown
|
|
if (yOffset < mRowHeight / 4)
|
|
*aOrient = nsIOutlinerView::inDropBefore;
|
|
else if (yOffset > mRowHeight - (mRowHeight / 4))
|
|
*aOrient = nsIOutlinerView::inDropAfter;
|
|
else
|
|
*aOrient = nsIOutlinerView::inDropOn;
|
|
}
|
|
else {
|
|
// for a non-container use a 50%/50% breakdown
|
|
if (yOffset < mRowHeight / 2)
|
|
*aOrient = nsIOutlinerView::inDropBefore;
|
|
else
|
|
*aOrient = nsIOutlinerView::inDropAfter;
|
|
}
|
|
}
|
|
} // ComputeDropPosition
|
|
|
|
// Determine if we're w/in a margin of the top/bottom of the outliner during a drag.
|
|
// This will ultimately cause us to scroll, but that's done elsewhere.
|
|
PRBool
|
|
nsOutlinerBodyFrame::IsInDragScrollRegion(nsIDOMEvent* aEvent, PRBool* aScrollUp)
|
|
{
|
|
PRBool isInRegion = PR_FALSE;
|
|
|
|
float pixelsToTwips = 0.0;
|
|
mPresContext->GetPixelsToTwips (&pixelsToTwips);
|
|
const int kMarginHeight = NSToIntRound(12 * pixelsToTwips);
|
|
|
|
nsCOMPtr<nsIDOMMouseEvent> mouseEvent (do_QueryInterface(aEvent));
|
|
if (mouseEvent) {
|
|
PRInt32 x = 0, y = 0;
|
|
mouseEvent->GetClientX(&x); mouseEvent->GetClientY(&y);
|
|
|
|
PRInt32 yTwips, xTwips;
|
|
AdjustEventCoordsToBoxCoordSpace (x, y, &xTwips, &yTwips);
|
|
|
|
if (yTwips < kMarginHeight) {
|
|
isInRegion = PR_TRUE;
|
|
if (aScrollUp)
|
|
*aScrollUp = PR_TRUE; // scroll up
|
|
}
|
|
else if (yTwips > mRect.height - kMarginHeight) {
|
|
isInRegion = PR_TRUE;
|
|
if (aScrollUp )
|
|
*aScrollUp = PR_FALSE; // scroll down
|
|
}
|
|
}
|
|
|
|
return isInRegion;
|
|
} // IsInDragScrollRegion
|
|
|
|
// Cache several things we'll need throughout the course of our work. These
|
|
// will all get released on a drag exit
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame::OnDragEnter(nsIDOMEvent* aEvent)
|
|
{
|
|
// cache the drag session
|
|
nsresult rv;
|
|
nsCOMPtr<nsIDragService> dragService =
|
|
do_GetService("@mozilla.org/widget/dragservice;1", &rv);
|
|
nsCOMPtr<nsIDragSession> dragSession;
|
|
dragService->GetCurrentSession(getter_AddRefs(mDragSession));
|
|
NS_ASSERTION(mDragSession, "can't get drag session");
|
|
|
|
return NS_OK;
|
|
} // OnDragEnter
|
|
|
|
|
|
#ifdef XP_MAC
|
|
#pragma mark -
|
|
#endif
|
|
|
|
|
|
// ==============================================================================
|
|
// The ImageListener implementation
|
|
// ==============================================================================
|
|
|
|
#ifdef USE_IMG2
|
|
NS_IMPL_ISUPPORTS3(nsOutlinerImageListener, imgIDecoderObserver, imgIContainerObserver, nsIOutlinerImageListener)
|
|
|
|
nsOutlinerImageListener::nsOutlinerImageListener(nsIOutlinerBoxObject* aOutliner, const PRUnichar* aID)
|
|
{
|
|
NS_INIT_ISUPPORTS();
|
|
mOutliner = aOutliner;
|
|
mColID = aID;
|
|
mMin = -1; // min should start out "undefined"
|
|
mMax = 0;
|
|
}
|
|
|
|
nsOutlinerImageListener::~nsOutlinerImageListener()
|
|
{
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerImageListener::OnStartDecode(imgIRequest *aRequest, nsISupports *aContext)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerImageListener::OnStartContainer(imgIRequest *aRequest, nsISupports *aContext, imgIContainer *aImage)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerImageListener::OnStartFrame(imgIRequest *aRequest, nsISupports *aContext, gfxIImageFrame *aFrame)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerImageListener::OnDataAvailable(imgIRequest *aRequest, nsISupports *aContext, gfxIImageFrame *aFrame, const nsRect *aRect)
|
|
{
|
|
Invalidate();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerImageListener::OnStopFrame(imgIRequest *aRequest, nsISupports *aContext, gfxIImageFrame *aFrame)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerImageListener::OnStopContainer(imgIRequest *aRequest, nsISupports *aContext, imgIContainer *aImage)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerImageListener::OnStopDecode(imgIRequest *aRequest, nsISupports *aContext, nsresult status, const PRUnichar *statusArg)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerImageListener::FrameChanged(imgIContainer *aContainer, nsISupports *aContext, gfxIImageFrame *newframe, nsRect * dirtyRect)
|
|
{
|
|
Invalidate();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsOutlinerImageListener::AddRow(int aIndex)
|
|
{
|
|
if (mMin == -1)
|
|
mMin = mMax = aIndex;
|
|
else if (aIndex < mMin)
|
|
mMin = aIndex;
|
|
else if (aIndex > mMax)
|
|
mMax = aIndex;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsOutlinerImageListener::Invalidate()
|
|
{
|
|
// Loop from min to max, invalidating each cell that was listening for this image.
|
|
for (PRInt32 i = mMin; i <= mMax; i++) {
|
|
mOutliner->InvalidateCell(i, mColID.get());
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
#endif
|
|
|
|
NS_IMETHODIMP_(void)
|
|
nsOutlinerBodyFrame::Notify(nsITimer* aTimer)
|
|
{
|
|
if (aTimer == mOpenTimer.get()) {
|
|
// open the node
|
|
mOpenTimer->Cancel();
|
|
mOpenTimer = nsnull;
|
|
if (mDropRow >= 0)
|
|
mView->ToggleOpenState(mDropRow);
|
|
}
|
|
}
|