2985 lines
100 KiB
C++
2985 lines
100 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>
|
|
*
|
|
* 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 "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"
|
|
|
|
#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));
|
|
|
|
// 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
|
|
const nsStyleVisibility* vis =
|
|
(const nsStyleVisibility*)styleContext->GetStyleData(eStyleStruct_Visibility);
|
|
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;
|
|
|
|
// 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;
|
|
}
|
|
|
|
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_END_INHERITING(nsLeafFrame)
|
|
|
|
|
|
|
|
// Constructor
|
|
nsOutlinerBodyFrame::nsOutlinerBodyFrame(nsIPresShell* aPresShell)
|
|
:nsLeafBoxFrame(aPresShell), mPresContext(nsnull), mOutlinerBoxObject(nsnull), mFocused(PR_FALSE), mImageCache(nsnull),
|
|
mColumns(nsnull), mScrollbar(nsnull), mTopRowIndex(0), mRowHeight(0), mIndentation(0),
|
|
mDropRow(kIllegalRow), mDropOrient(kNoOrientation), mDropAllowed(PR_FALSE), mIsSortRectDrawn(PR_FALSE),
|
|
mAlreadyUndrewDueToScroll(PR_FALSE)
|
|
{
|
|
NS_NewISupportsArray(getter_AddRefs(mScratchArray));
|
|
mColumnsDirty = PR_TRUE;
|
|
}
|
|
|
|
// 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_IID(kWidgetCID, NS_CHILD_CID);
|
|
|
|
ourView->CreateWidget(kWidgetCID);
|
|
ourView->GetWidget(*getter_AddRefs(mOutlinerWidget));
|
|
return rv;
|
|
}
|
|
|
|
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.AssignWithConversion("topRow");
|
|
nsAutoString topRow;
|
|
topRow.AppendInt(mTopRowIndex);
|
|
box->SetProperty(topRowStr.get(), topRow.get());
|
|
}
|
|
|
|
// Always null out the cached outliner body frame.
|
|
nsAutoString outlinerBody; outlinerBody.AssignWithConversion("outlinerbody");
|
|
box->RemoveProperty(outlinerBody.get());
|
|
|
|
mOutlinerBoxObject = nsnull; // Drop our ref here.
|
|
}
|
|
|
|
mView = nsnull;
|
|
|
|
return nsLeafBoxFrame::Destroy(aPresContext);
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::Reflow(nsIPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aReflowMetrics,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
if (aReflowState.reason == eReflowReason_Initial) {
|
|
// We might have a box object with some properties already cached. If so,
|
|
// pull them out of the box object and restore them here.
|
|
mRowHeight = GetRowHeight();
|
|
nsCOMPtr<nsIContent> parent;
|
|
mContent->GetParent(*getter_AddRefs(parent));
|
|
nsCOMPtr<nsIDOMXULElement> parentXUL(do_QueryInterface(parent));
|
|
if (parentXUL) {
|
|
nsCOMPtr<nsIBoxObject> box;
|
|
parentXUL->GetBoxObject(getter_AddRefs(box));
|
|
if (box) {
|
|
nsCOMPtr<nsIOutlinerBoxObject> outlinerBox(do_QueryInterface(box));
|
|
SetBoxObject(outlinerBox);
|
|
|
|
nsAutoString view; view.AssignWithConversion("view");
|
|
nsCOMPtr<nsISupports> suppView;
|
|
box->GetPropertyAsSupports(view.get(), getter_AddRefs(suppView));
|
|
nsCOMPtr<nsIOutlinerView> outlinerView(do_QueryInterface(suppView));
|
|
|
|
if (outlinerView) {
|
|
nsAutoString topRow; topRow.AssignWithConversion("topRow");
|
|
nsXPIDLString rowStr;
|
|
box->GetProperty(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.
|
|
ScrollToRow(rowIndex);
|
|
|
|
// Clear out the property info for the top row, but we always keep the
|
|
// view current.
|
|
box->RemoveProperty(topRow.get());
|
|
|
|
return nsLeafBoxFrame::Reflow(aPresContext, aReflowMetrics, aReflowState, aStatus);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// A content model view is always created and hooked up,
|
|
// unless there is a XULOutlinerBuilder view.
|
|
|
|
nsCOMPtr<nsIDOMXULElement> xulele = do_QueryInterface(mContent);
|
|
if (xulele) {
|
|
nsCOMPtr<nsIOutlinerView> view;
|
|
|
|
// First, see if there is a XUL outliner builder
|
|
// associated with the element.
|
|
nsCOMPtr<nsIXULTemplateBuilder> builder;
|
|
xulele->GetBuilder(getter_AddRefs(builder));
|
|
if (builder)
|
|
view = do_QueryInterface(builder);
|
|
else {
|
|
// No builder, create a outliner content model view.
|
|
nsCOMPtr<nsIOutlinerContentView> contentView;
|
|
NS_NewOutlinerContentView(getter_AddRefs(contentView));
|
|
if (contentView) {
|
|
contentView->SetRoot(xulele);
|
|
view = do_QueryInterface(contentView);
|
|
}
|
|
}
|
|
|
|
// Hook up the view.
|
|
if (view)
|
|
SetView(view);
|
|
}
|
|
}
|
|
|
|
if (mView && mRowHeight && aReflowState.reason == eReflowReason_Resize) {
|
|
mInnerBox = GetInnerBox();
|
|
mPageCount = mInnerBox.height / mRowHeight;
|
|
|
|
PRInt32 rowCount;
|
|
mView->GetRowCount(&rowCount);
|
|
PRInt32 lastPageTopRow = rowCount - mPageCount;
|
|
if (mTopRowIndex >= lastPageTopRow)
|
|
ScrollToRow(lastPageTopRow);
|
|
|
|
InvalidateScrollbar();
|
|
SetVisibleScrollbar((rowCount >= mPageCount));
|
|
}
|
|
|
|
return nsLeafBoxFrame::Reflow(aPresContext, aReflowMetrics, aReflowState, aStatus);
|
|
}
|
|
|
|
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.
|
|
nsCOMPtr<nsIBoxObject> box(do_QueryInterface(mOutlinerBoxObject));
|
|
if (!box)
|
|
return NS_OK; // Just ignore the call. An initial reflow when it comes in
|
|
// will retrieve the view from the box object.
|
|
|
|
nsAutoString view; view.AssignWithConversion("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(this, getter_AddRefs(sel));
|
|
mView->SetSelection(sel);
|
|
}
|
|
|
|
// The scrollbar will need to be updated.
|
|
InvalidateScrollbar();
|
|
|
|
// Reset scrollbar position.
|
|
UpdateScrollbar();
|
|
|
|
PRInt32 rowCount;
|
|
mView->GetRowCount(&rowCount);
|
|
SetVisibleScrollbar((rowCount >= mPageCount));
|
|
}
|
|
|
|
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::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, PR_FALSE);
|
|
}
|
|
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, PR_FALSE);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::InvalidateCell(PRInt32 aIndex, const PRUnichar *aColID)
|
|
{
|
|
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()) {
|
|
nsRect cellRect(currX, yPos, currCol->GetWidth(), mRowHeight);
|
|
|
|
if (nsCRT::strcmp(currCol->GetID(), aColID) == 0) {
|
|
nsLeafBoxFrame::Invalidate(mPresContext, cellRect, 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, 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::SetVisibleScrollbar(PRBool aSetVisible)
|
|
{
|
|
NS_ASSERTION(mScrollbar, "no scroll bar");
|
|
if (!mScrollbar)
|
|
return NS_OK;
|
|
|
|
nsCOMPtr<nsIContent> scrollbarContent;
|
|
mScrollbar->GetContent(getter_AddRefs(scrollbarContent));
|
|
|
|
nsAutoString isCollapsed;
|
|
scrollbarContent->GetAttr(kNameSpaceID_None, nsXULAtoms::collapsed,
|
|
isCollapsed);
|
|
|
|
if (!isCollapsed.IsEmpty() && aSetVisible)
|
|
scrollbarContent->UnsetAttr(kNameSpaceID_None, nsXULAtoms::collapsed,
|
|
PR_TRUE);
|
|
else if (isCollapsed.IsEmpty() && !aSetVisible)
|
|
scrollbarContent->SetAttr(kNameSpaceID_None, nsXULAtoms::collapsed,
|
|
NS_LITERAL_STRING("true"), PR_TRUE);
|
|
else
|
|
return NS_OK;
|
|
|
|
Invalidate();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::InvalidateScrollbar()
|
|
{
|
|
if (!mScrollbar) {
|
|
// Try to find it.
|
|
nsCOMPtr<nsIContent> parContent;
|
|
mContent->GetParent(*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;
|
|
}
|
|
|
|
|
|
//
|
|
// AdjustEventCoordsToBoxCoordSpace
|
|
//
|
|
// Takes client x/y in pixels, converts them to twips, and massages them to be
|
|
// in our coordinate system.
|
|
//
|
|
void
|
|
nsOutlinerBodyFrame :: AdjustEventCoordsToBoxCoordSpace ( PRInt32 inX, PRInt32 inY, PRInt32* outX, PRInt32* outY )
|
|
{
|
|
// Convert our x and y coords to twips.
|
|
float pixelsToTwips = 0.0;
|
|
mPresContext->GetPixelsToTwips(&pixelsToTwips);
|
|
inX = NSToIntRound(inX * pixelsToTwips);
|
|
inY = NSToIntRound(inY * 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);
|
|
|
|
// Adjust into our coordinate space.
|
|
x = inX-x;
|
|
y = inY-y;
|
|
|
|
// Adjust y by the inner box y, so that we're in the inner box's
|
|
// coordinate space.
|
|
y += mInnerBox.y;
|
|
|
|
*outX = x;
|
|
*outY = y;
|
|
|
|
} // AdjustEventCoordsToBoxCoordSpace
|
|
|
|
|
|
NS_IMETHODIMP nsOutlinerBodyFrame::GetCellAt(PRInt32 aX, PRInt32 aY, PRInt32* aRow, PRUnichar** aColID,
|
|
PRUnichar** aChildElt)
|
|
{
|
|
// 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;
|
|
|
|
// 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 = nsCRT::strdup(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);
|
|
|
|
nsAutoString colID;
|
|
currCol->GetID(colID);
|
|
// 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 (!colID.EqualsWithConversion(aColID)) {
|
|
currX += cellRect.width;
|
|
continue;
|
|
}
|
|
|
|
// Now obtain the properties for our cell.
|
|
PrefillPropertyArray(aRow, currCol);
|
|
mView->GetCellProperties(aRow, currCol->GetID(), 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; cell.AssignWithConversion("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;
|
|
|
|
// Start the Twisty Rect as the full width of the cell, and gradually decrement its width
|
|
// as we figure out the size of other elements.
|
|
nsRect twistyRect(cellX, cellRect.y, remainWidth, cellRect.height);
|
|
|
|
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(), 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(), 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
|
|
nsXPIDLString text;
|
|
mView->GetCellText(aRow, currCol->GetID(), getter_Copies(text));
|
|
nsAutoString cellText(text);
|
|
|
|
// 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(), 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(), 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(), 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;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame::IsCellCropped(PRInt32 aRow, const nsAString& aColID, PRBool *_retval)
|
|
{
|
|
nsOutlinerColumn* currCol = nsnull;
|
|
// Keep looping until we find a column with a matching Id.
|
|
for (currCol = mColumns; currCol; currCol = currCol->GetNext()) {
|
|
nsAutoString colID;
|
|
currCol->GetID(colID);
|
|
if (colID.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));
|
|
AdjustForBorderPadding(cellContext, cellRect);
|
|
|
|
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);
|
|
remainWidth -= 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(), 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);
|
|
|
|
remainWidth -= 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(), imageContext);
|
|
remainWidth -= imageSize.width;
|
|
|
|
// Get the cell text.
|
|
nsXPIDLString text;
|
|
mView->GetCellText(aRow, currCol->GetID(), getter_Copies(text));
|
|
nsAutoString cellText(text);
|
|
|
|
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;
|
|
|
|
// If |totalTextWidth| is greater than |remainWidth|, then we are cropping.
|
|
*_retval = totalTextWidth > remainWidth;
|
|
}
|
|
|
|
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();
|
|
SetVisibleScrollbar((rowCount >= mPageCount));
|
|
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();
|
|
SetVisibleScrollbar((rowCount >= mPageCount));
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsOutlinerBodyFrame::PrefillPropertyArray(PRInt32 aRowIndex, nsOutlinerColumn* aCol)
|
|
{
|
|
mScratchArray->Clear();
|
|
|
|
// focus
|
|
if (mFocused)
|
|
mScratchArray->AppendElement(nsXULAtoms::focus);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
if (aCol) {
|
|
nsCOMPtr<nsIAtom> colID;
|
|
aCol->GetIDAtom(getter_AddRefs(colID));
|
|
mScratchArray->AppendElement(colID);
|
|
}
|
|
}
|
|
|
|
#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));
|
|
il->LoadImage(srcURI, nsnull, listener, mPresContext, nsIRequest::LOAD_NORMAL, nsnull, nsnull, getter_AddRefs(imageRequest));
|
|
|
|
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);
|
|
if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord) {
|
|
PRInt32 val = myPosition->mWidth.GetCoordValue();
|
|
r.width += val;
|
|
}
|
|
else
|
|
needWidth = PR_TRUE;
|
|
|
|
if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord) {
|
|
PRInt32 val = myPosition->mHeight.GetCoordValue();
|
|
r.height += val;
|
|
}
|
|
else
|
|
needHeight = PR_TRUE;
|
|
|
|
if (needWidth || needHeight) {
|
|
#ifdef USE_IMG2
|
|
nsCOMPtr<imgIContainer> image;
|
|
GetImage(aRowIndex, aColID, aStyleContext, getter_AddRefs(image));
|
|
// Get the natural image size.
|
|
if (image) {
|
|
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;
|
|
}
|
|
}
|
|
return 19*15; // 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;
|
|
}
|
|
}
|
|
return 16*15; // 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(), elt, mScratchArray);
|
|
|
|
// Read special properties from attributes on the column content node
|
|
nsAutoString attr;
|
|
aColumn->GetElement()->GetAttr(kNameSpaceID_None, nsXULAtoms::insertbefore, attr);
|
|
if (attr.EqualsWithConversion("true"))
|
|
mScratchArray->AppendElement(nsXULAtoms::insertbefore);
|
|
attr.AssignWithConversion("");
|
|
aColumn->GetElement()->GetAttr(kNameSpaceID_None, nsXULAtoms::insertafter, attr);
|
|
if (attr.EqualsWithConversion("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 (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer)
|
|
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(), 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;
|
|
|
|
// Always leave space for the twisty.
|
|
nsRect twistyRect(currX, cellRect.y, remainingWidth, cellRect.height);
|
|
PaintTwisty(aRowIndex, aColumn, twistyRect, aPresContext, aRenderingContext, aDirtyRect, aWhichLayer,
|
|
remainingWidth, currX);
|
|
|
|
// 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.
|
|
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));
|
|
|
|
PRInt32 x;
|
|
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(), mScratchArray);
|
|
|
|
nsCOMPtr<nsIStyleContext> imageContext;
|
|
GetPseudoStyleContext(nsXULAtoms::mozoutlinerimage, getter_AddRefs(imageContext));
|
|
|
|
nsRect imageSize = GetImageSize(currentParent, aColumn->GetID(), imageContext);
|
|
|
|
const nsStyleMargin* imageMarginData = (const nsStyleMargin*)imageContext->GetStyleData(eStyleStruct_Margin);
|
|
nsMargin imageMargin;
|
|
imageMarginData->GetMargin(imageMargin);
|
|
imageSize.Inflate(imageMargin);
|
|
|
|
// Line up line with the parent image.
|
|
x = currX + 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, y + mRowHeight /2);
|
|
|
|
PRBool clipState;
|
|
aRenderingContext.PopState(clipState);
|
|
|
|
PrefillPropertyArray(aRowIndex, aColumn);
|
|
mView->GetCellProperties(aRowIndex, aColumn->GetID(), mScratchArray);
|
|
}
|
|
}
|
|
|
|
// 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));
|
|
|
|
// 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.
|
|
nsRect imageSize = GetImageSize(aRowIndex, aColumn->GetID(), twistyContext);
|
|
twistyRect.width = imageSize.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) {
|
|
// Time to paint the twisty.
|
|
// Adjust the rect for its border and padding.
|
|
AdjustForBorderPadding(twistyContext, twistyRect);
|
|
AdjustForBorderPadding(twistyContext, imageSize);
|
|
|
|
#ifdef USE_IMG2
|
|
// Get the image for drawing.
|
|
nsCOMPtr<imgIContainer> image;
|
|
GetImage(aRowIndex, aColumn->GetID(), 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;
|
|
if (((twistyRect.height - imageSize.height)/15)%2 != 0)
|
|
p.y -= 15;
|
|
}
|
|
|
|
// Paint the image.
|
|
aRenderingContext.DrawImage(image, &imageSize, &p);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
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(), 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(), imageContext, getter_AddRefs(image));
|
|
if (image) {
|
|
nsPoint p(imageRect.x, imageRect.y);
|
|
|
|
// Center the image. XXX Obey vertical-align style prop?
|
|
if (imageSize.height < imageRect.height) {
|
|
p.y += (imageRect.height - imageSize.height)/2;
|
|
if (((imageRect.height - imageSize.height)/15)%2 != 0)
|
|
p.y -= 15; // 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 (((imageRect.width - imageSize.width)/15)%2 != 0)
|
|
p.x -= 15; // 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.
|
|
nsXPIDLString text;
|
|
mView->GetCellText(aRowIndex, aColumn->GetID(), getter_Copies(text));
|
|
|
|
nsAutoString realText(text);
|
|
|
|
if (realText.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);
|
|
|
|
// 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);
|
|
|
|
// 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(realText, 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);
|
|
|
|
nsAutoString ellipsis; ellipsis.AssignWithConversion(ELLIPSIS);
|
|
|
|
nscoord width = textRect.width;
|
|
if (ellipsisWidth > width)
|
|
realText.SetLength(0);
|
|
else if (ellipsisWidth == width)
|
|
realText = 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 = realText.Length();
|
|
int i;
|
|
for (i = 0; i < length; ++i) {
|
|
PRUnichar ch = realText.CharAt(i);
|
|
aRenderingContext.GetWidth(ch,cwidth);
|
|
if (twidth + cwidth > width)
|
|
break;
|
|
twidth += cwidth;
|
|
}
|
|
|
|
realText.Truncate(i);
|
|
realText += ellipsis;
|
|
}
|
|
break;
|
|
|
|
case 2: {
|
|
// Crop left.
|
|
nscoord cwidth;
|
|
nscoord twidth = 0;
|
|
int length = realText.Length();
|
|
int i;
|
|
for (i=length-1; i >= 0; --i) {
|
|
PRUnichar ch = realText.CharAt(i);
|
|
aRenderingContext.GetWidth(ch,cwidth);
|
|
if (twidth + cwidth > width)
|
|
break;
|
|
|
|
twidth += cwidth;
|
|
}
|
|
|
|
nsAutoString copy;
|
|
realText.Right(copy, length-1-i);
|
|
realText = ellipsis;
|
|
realText += copy;
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
{
|
|
// XXX Not yet implemented.
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set our color.
|
|
const nsStyleColor* colorStyle = (const nsStyleColor*)textContext->GetStyleData(eStyleStruct_Color);
|
|
aRenderingContext.SetColor(colorStyle->mColor);
|
|
|
|
aRenderingContext.DrawString(realText, 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::PaintBackground(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();
|
|
|
|
#ifdef XP_MAC
|
|
// mac can't process the event loop during a drag, so if we're dragging,
|
|
// grab the scroll widget and make it paint synchronously. This is
|
|
// sorta slow (having to paint the entire tree), but it works.
|
|
if ( mDragSession ) {
|
|
nsCOMPtr<nsIWidget> scrollWidget;
|
|
mScrollbar->GetWindow(mPresContext, getter_AddRefs(scrollWidget));
|
|
if ( scrollWidget )
|
|
scrollWidget->Invalidate(PR_TRUE);
|
|
}
|
|
#endif
|
|
|
|
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);
|
|
|
|
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;
|
|
mContent->GetParent(*getter_AddRefs(parent));
|
|
nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(parent));
|
|
|
|
nsCOMPtr<nsIDOMNodeList> cols;
|
|
elt->GetElementsByTagNameNS(NS_LITERAL_STRING("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"),
|
|
NS_LITERAL_STRING("outlinercol"),
|
|
getter_AddRefs(cols));
|
|
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
mPresContext->GetShell(getter_AddRefs(shell));
|
|
|
|
PRUint32 count;
|
|
cols->GetLength(&count);
|
|
|
|
if (count == 0)
|
|
return; // Nothing to do.
|
|
|
|
nsIFrame* frame = nsnull;
|
|
nsIFrame* colContainer = nsnull;
|
|
PRInt32 i = 0;
|
|
do {
|
|
// Get a column, and get the parent frame. We need to use its box direction
|
|
// to find out the order in which we should iterate the columns.
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
cols->Item(i++, getter_AddRefs(node));
|
|
nsCOMPtr<nsIContent> child(do_QueryInterface(node));
|
|
|
|
// Get the frame for this column.
|
|
shell->GetPrimaryFrameFor(child, &frame);
|
|
if (frame)
|
|
frame->GetParent(&colContainer);
|
|
} while (!frame);
|
|
|
|
if (!colContainer)
|
|
return;
|
|
|
|
nsCOMPtr<nsIBox> colContainerBox(do_QueryInterface(colContainer));
|
|
nsIBox* colBox = nsnull;
|
|
colContainerBox->GetChildBox(&colBox);
|
|
|
|
nsOutlinerColumn* currCol = nsnull;
|
|
while (colBox) {
|
|
nsIFrame* frame = nsnull;
|
|
colBox->GetFrame(&frame);
|
|
nsCOMPtr<nsIContent> content;
|
|
frame->GetContent(getter_AddRefs(content));
|
|
nsCOMPtr<nsIDOMElement> colElt(do_QueryInterface(content));
|
|
nsCOMPtr<nsIDOMNode> colParentElt;
|
|
colElt->GetParentNode(getter_AddRefs(colParentElt));
|
|
if (colParentElt == elt) {
|
|
// Create a new column structure.
|
|
nsOutlinerColumn* col = new nsOutlinerColumn(content, frame);
|
|
if (currCol)
|
|
currCol->SetNext(col);
|
|
else mColumns = col;
|
|
currCol = col;
|
|
}
|
|
|
|
colBox->GetNextBox(&colBox);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef XP_MAC
|
|
#pragma mark -
|
|
#endif
|
|
|
|
|
|
//
|
|
// OnDragDrop
|
|
//
|
|
// Tell the view where the drop happened
|
|
//
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame :: OnDragDrop ( nsIDOMEvent* inEvent )
|
|
{
|
|
mView->Drop ( mDropRow, mDropOrient );
|
|
return NS_OK;
|
|
|
|
} // OnDragDrop
|
|
|
|
|
|
//
|
|
// OnDragExit
|
|
//
|
|
// Clear out all our tracking vars. If we were drawing feedback, undraw it
|
|
//
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame :: OnDragExit ( nsIDOMEvent* inEvent )
|
|
{
|
|
if ( mDropAllowed && !mAlreadyUndrewDueToScroll )
|
|
DrawDropFeedback ( mDropRow, mDropOrient, kUndrawFeedback ) ;
|
|
|
|
mDropRow = kIllegalRow;
|
|
mDropOrient = kNoOrientation;
|
|
mDropAllowed = PR_FALSE;
|
|
mIsSortRectDrawn = PR_FALSE;
|
|
mAlreadyUndrewDueToScroll = PR_FALSE;
|
|
|
|
mDragSession = nsnull;
|
|
mRenderingContext = nsnull;
|
|
return NS_OK;
|
|
|
|
} // OnDragExit
|
|
|
|
|
|
//
|
|
// OnDragOver
|
|
//
|
|
// The mouse is hovering over this outliner. If we determine things are different from the
|
|
// last time, undraw feedback at the old position, query the view to see if the current location is
|
|
// droppable, and then draw feedback 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.
|
|
//
|
|
NS_IMETHODIMP
|
|
nsOutlinerBodyFrame :: OnDragOver ( nsIDOMEvent* inEvent )
|
|
{
|
|
// while we're here, handle tracking of scrolling during a drag. There is a little craziness
|
|
// here as we turn off tracking of feedback during the scroll. When we first start scrolling,
|
|
// we explicitly undraw the previous feedback, then set |mAlreadyUndrewDueToScroll| to
|
|
// alert other places not to undraw again later (we're using XOR, so undrawing twice
|
|
// is bad). Below, we'll clear this member the next time we try to undraw the regular feedback.
|
|
PRBool scrollUp = PR_FALSE;
|
|
if ( IsInDragScrollRegion(inEvent, &scrollUp) ) {
|
|
if ( mDropAllowed && !mAlreadyUndrewDueToScroll )
|
|
DrawDropFeedback ( mDropRow, mDropOrient, kUndrawFeedback ); // undraw it at old loc, if we were drawing
|
|
mAlreadyUndrewDueToScroll = PR_TRUE;
|
|
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 = kIllegalRow;
|
|
DropOrientation newOrient = kNoOrientation;
|
|
ComputeDropPosition ( inEvent, &newRow, &newOrient );
|
|
|
|
// if changed from last time, undraw it at the old location and if allowed,
|
|
// draw it at the new location. If nothing changed, just bail.
|
|
if ( newRow != mDropRow || newOrient != mDropOrient ) {
|
|
|
|
// undraw feedback at old loc. If we are coming off a scroll,
|
|
// don't undraw the old (we already did that), but reset us so that
|
|
// we're back in the normal case.
|
|
if ( !mAlreadyUndrewDueToScroll ) {
|
|
if ( mDropAllowed )
|
|
DrawDropFeedback ( mDropRow, mDropOrient, kUndrawFeedback );
|
|
}
|
|
else
|
|
mAlreadyUndrewDueToScroll = PR_FALSE;
|
|
|
|
// cache the new row and orientation regardless so we can check if it changed
|
|
// for next time.
|
|
mDropRow = newRow;
|
|
mDropOrient = newOrient;
|
|
|
|
PRBool canDropAtNewLocation = PR_FALSE;
|
|
if ( newOrient == kOnRow )
|
|
mView->CanDropOn ( newRow, &canDropAtNewLocation );
|
|
else
|
|
mView->CanDropBeforeAfter ( newRow, newOrient == kBeforeRow ? PR_TRUE : PR_FALSE, &canDropAtNewLocation );
|
|
|
|
if ( canDropAtNewLocation )
|
|
DrawDropFeedback ( newRow, newOrient, kDrawFeedback ); // draw it at old loc, if we are allowed
|
|
|
|
mDropAllowed = canDropAtNewLocation;
|
|
}
|
|
|
|
// 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
|
|
|
|
|
|
//
|
|
// DrawDropFeedback
|
|
//
|
|
// Takes care of actually drawing the correct feedback. |inDrawFeedback| tells us whether
|
|
// we're drawing or undrawing (removing/clearing) the feedback for the given row.
|
|
//
|
|
// XXX Need to be able to make line color respect style
|
|
//
|
|
void
|
|
nsOutlinerBodyFrame :: DrawDropFeedback ( PRInt32 inDropRow, DropOrientation inDropOrient, PRBool inDrawFeedback )
|
|
{
|
|
// call appropriate routine (insert, container, etc) based on |inDropOrient| and pass in |inDrawFeedback|
|
|
float pixelsToTwips = 0.0;
|
|
mPresContext->GetPixelsToTwips ( &pixelsToTwips );
|
|
|
|
#if NOT_YET_WORKING
|
|
// feedback will differ depending on if we're sorted or not -- leaving this around
|
|
// for later.
|
|
if ( viewSorted ) {
|
|
PRInt32 penSize = NSToIntRound(1*pixelsToTwips); // use a 1 pixel wide pen
|
|
|
|
// draw outline rectangle made of 4 rects (gfx can't just frame a rectangle). The
|
|
// rects can't overlap because we're XORing.
|
|
mRenderingContext->InvertRect ( 0, 0, mRect.width, penSize ); // top
|
|
mRenderingContext->InvertRect ( 0, penSize, penSize, mRect.height ); // left
|
|
mRenderingContext->InvertRect ( mRect.width - penSize, penSize, penSize, mRect.height ); // right
|
|
mRenderingContext->InvertRect ( penSize, mRect.height - penSize,
|
|
mRect.width - 2*penSize, penSize ); // bottom
|
|
}
|
|
#endif
|
|
|
|
nsOutlinerColumn* primaryCol = nsnull;
|
|
for (nsOutlinerColumn* currCol = mColumns; currCol; currCol = currCol->GetNext()) {
|
|
if ( currCol->IsPrimary() ) {
|
|
primaryCol = currCol;
|
|
break;
|
|
}
|
|
}
|
|
if ( !primaryCol )
|
|
return;
|
|
|
|
if ( inDropOrient == kOnRow ) {
|
|
// drawing "on" a row. Invert the image and text in the primary column.
|
|
PRInt32 x, y, width, height;
|
|
GetCoordsForCellItem ( inDropRow, primaryCol->GetID(), NS_LITERAL_STRING("image").get(),
|
|
&x, &y, &width, &height );
|
|
mRenderingContext->InvertRect ( NSToIntRound(x*pixelsToTwips), NSToIntRound(y*pixelsToTwips),
|
|
NSToIntRound(width*pixelsToTwips), NSToIntRound(height*pixelsToTwips) );
|
|
GetCoordsForCellItem ( inDropRow, primaryCol->GetID(), NS_LITERAL_STRING("text").get(),
|
|
&x, &y, &width, &height );
|
|
mRenderingContext->InvertRect ( NSToIntRound(x*pixelsToTwips), NSToIntRound(y*pixelsToTwips),
|
|
NSToIntRound(width*pixelsToTwips), NSToIntRound(height*pixelsToTwips) );
|
|
}
|
|
else {
|
|
// drawing between rows, find the X/Y to draw a 2 pixel line indented 5 pixels
|
|
// from the left of the image in the primary column.
|
|
PRInt32 whereToDrawY = mRowHeight * (inDropRow - mTopRowIndex);
|
|
if ( inDropOrient == kAfterRow )
|
|
whereToDrawY += mRowHeight;
|
|
|
|
PRInt32 whereToDrawX = 0;
|
|
PRInt32 y, width, height;
|
|
GetCoordsForCellItem ( inDropRow, primaryCol->GetID(), NS_LITERAL_STRING("image").get(),
|
|
&whereToDrawX, &y, &width, &height );
|
|
whereToDrawX += 5; // indent 5 pixels from left of image
|
|
|
|
mRenderingContext->InvertRect ( NSToIntRound(whereToDrawX*pixelsToTwips),
|
|
whereToDrawY, NSToIntRound(25*pixelsToTwips), NSToIntRound(2*pixelsToTwips) );
|
|
}
|
|
|
|
} // DrawDropFeedback
|
|
|
|
|
|
//
|
|
// ComputeDropPosition
|
|
//
|
|
// 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* inEvent, PRInt32* outRow, DropOrientation* outOrient )
|
|
{
|
|
nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(inEvent) );
|
|
if ( mouseEvent ) {
|
|
PRInt32 x = 0, y = 0;
|
|
mouseEvent->GetClientX(&x); mouseEvent->GetClientY(&y);
|
|
|
|
PRInt32 row = kIllegalRow;
|
|
nsXPIDLString colID, child;
|
|
GetCellAt ( x, y, &row, getter_Copies(colID), getter_Copies(child) );
|
|
|
|
// GetCellAt() will just blindly report a row even if there is no content there
|
|
// (ie, dragging below the end of the tree). If that's the case, set the reported row
|
|
// to after the final row. If we're not off the end, then check the coords w/in the cell
|
|
// to see if we are dropping before/on/after.
|
|
PRInt32 totalNumRows = 0;
|
|
mView->GetRowCount ( &totalNumRows );
|
|
if ( row > totalNumRows - 1 ) { // doh, we're off the end of the tree
|
|
row = (totalNumRows-1) - mTopRowIndex;
|
|
*outOrient = kAfterRow;
|
|
}
|
|
else { // w/in a cell, check above/below/on
|
|
// 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 rowTop = mRowHeight * (row - mTopRowIndex);
|
|
PRInt32 rowBottom = rowTop + mRowHeight;
|
|
PRInt32 yOffset = yTwips - rowTop;
|
|
|
|
PRBool isContainer = PR_FALSE;
|
|
mView->IsContainer ( row, &isContainer );
|
|
if ( isContainer ) {
|
|
// for a container, use a 25%/50%/25% breakdown
|
|
if ( yOffset < mRowHeight / 4 )
|
|
*outOrient = kBeforeRow;
|
|
else if ( yOffset > mRowHeight - (mRowHeight / 4) )
|
|
*outOrient = kAfterRow;
|
|
else
|
|
*outOrient = kOnRow;
|
|
}
|
|
else {
|
|
// for a non-container use a 50%/50% breakdown
|
|
if ( yOffset < mRowHeight / 2 )
|
|
*outOrient = kBeforeRow;
|
|
else
|
|
*outOrient = kAfterRow;
|
|
}
|
|
}
|
|
|
|
*outRow = row;
|
|
}
|
|
|
|
} // ComputeDropPosition
|
|
|
|
|
|
//
|
|
// IsInDragScrollRegion
|
|
//
|
|
// 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* inEvent, PRBool* outScrollUp )
|
|
{
|
|
PRBool isInRegion = PR_FALSE;
|
|
|
|
float pixelsToTwips = 0.0;
|
|
mPresContext->GetPixelsToTwips ( &pixelsToTwips );
|
|
const int kMarginHeight = NSToIntRound ( 12 * pixelsToTwips );
|
|
|
|
nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(inEvent) );
|
|
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 ( outScrollUp )
|
|
*outScrollUp = PR_TRUE; // scroll up
|
|
}
|
|
else if ( yTwips > mRect.height - kMarginHeight ) {
|
|
isInRegion = PR_TRUE;
|
|
if ( outScrollUp )
|
|
*outScrollUp = PR_FALSE; // scroll down
|
|
}
|
|
}
|
|
|
|
return isInRegion;
|
|
|
|
} // IsInDragScrollRegion
|
|
|
|
|
|
//
|
|
// OnDragEnter
|
|
//
|
|
// 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* inEvent )
|
|
{
|
|
// create a rendering context for our drawing needs
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
mPresContext->GetShell(getter_AddRefs(presShell));
|
|
nsCOMPtr<nsIRenderingContext> rendContext;
|
|
presShell->CreateRenderingContext ( this, getter_AddRefs(mRenderingContext) );
|
|
|
|
// 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
|
|
|