summaryrefslogtreecommitdiff
path: root/layout/xul/nsSplitterFrame.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/xul/nsSplitterFrame.cpp')
-rw-r--r--layout/xul/nsSplitterFrame.cpp1046
1 files changed, 1046 insertions, 0 deletions
diff --git a/layout/xul/nsSplitterFrame.cpp b/layout/xul/nsSplitterFrame.cpp
new file mode 100644
index 0000000000..7879a176d3
--- /dev/null
+++ b/layout/xul/nsSplitterFrame.cpp
@@ -0,0 +1,1046 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//
+// Eric Vaughan
+// Netscape Communications
+//
+// See documentation in associated header file
+//
+
+#include "nsSplitterFrame.h"
+#include "nsGkAtoms.h"
+#include "nsIDOMElement.h"
+#include "nsIDOMXULElement.h"
+#include "nsPresContext.h"
+#include "nsRenderingContext.h"
+#include "nsIDocument.h"
+#include "nsNameSpaceManager.h"
+#include "nsScrollbarButtonFrame.h"
+#include "nsIDOMEventListener.h"
+#include "nsIDOMMouseEvent.h"
+#include "nsIPresShell.h"
+#include "nsFrameList.h"
+#include "nsHTMLParts.h"
+#include "nsStyleContext.h"
+#include "nsBoxLayoutState.h"
+#include "nsIServiceManager.h"
+#include "nsContainerFrame.h"
+#include "nsContentCID.h"
+#include "mozilla/StyleSetHandle.h"
+#include "mozilla/StyleSetHandleInlines.h"
+#include "nsLayoutUtils.h"
+#include "nsDisplayList.h"
+#include "nsContentUtils.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/Event.h"
+#include "mozilla/MouseEvents.h"
+#include "mozilla/UniquePtr.h"
+
+using namespace mozilla;
+
+class nsSplitterInfo {
+public:
+ nscoord min;
+ nscoord max;
+ nscoord current;
+ nscoord changed;
+ nsCOMPtr<nsIContent> childElem;
+ int32_t flex;
+ int32_t index;
+};
+
+class nsSplitterFrameInner final : public nsIDOMEventListener
+{
+protected:
+ virtual ~nsSplitterFrameInner();
+
+public:
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOMEVENTLISTENER
+
+ explicit nsSplitterFrameInner(nsSplitterFrame* aSplitter)
+ {
+ mOuter = aSplitter;
+ mPressed = false;
+ }
+
+ void Disconnect() { mOuter = nullptr; }
+
+ nsresult MouseDown(nsIDOMEvent* aMouseEvent);
+ nsresult MouseUp(nsIDOMEvent* aMouseEvent);
+ nsresult MouseMove(nsIDOMEvent* aMouseEvent);
+
+ void MouseDrag(nsPresContext* aPresContext, WidgetGUIEvent* aEvent);
+ void MouseUp(nsPresContext* aPresContext, WidgetGUIEvent* aEvent);
+
+ void AdjustChildren(nsPresContext* aPresContext);
+ void AdjustChildren(nsPresContext* aPresContext, nsSplitterInfo* aChildInfos, int32_t aCount, bool aIsHorizontal);
+
+ void AddRemoveSpace(nscoord aDiff,
+ nsSplitterInfo* aChildInfos,
+ int32_t aCount,
+ int32_t& aSpaceLeft);
+
+ void ResizeChildTo(nscoord& aDiff,
+ nsSplitterInfo* aChildrenBeforeInfos,
+ nsSplitterInfo* aChildrenAfterInfos,
+ int32_t aChildrenBeforeCount,
+ int32_t aChildrenAfterCount,
+ bool aBounded);
+
+ void UpdateState();
+
+ void AddListener();
+ void RemoveListener();
+
+ enum ResizeType { Closest, Farthest, Flex, Grow };
+ enum State { Open, CollapsedBefore, CollapsedAfter, Dragging };
+ enum CollapseDirection { Before, After };
+
+ ResizeType GetResizeBefore();
+ ResizeType GetResizeAfter();
+ State GetState();
+
+ void Reverse(UniquePtr<nsSplitterInfo[]>& aIndexes, int32_t aCount);
+ bool SupportsCollapseDirection(CollapseDirection aDirection);
+
+ void EnsureOrient();
+ void SetPreferredSize(nsBoxLayoutState& aState, nsIFrame* aChildBox, nscoord aOnePixel, bool aIsHorizontal, nscoord* aSize);
+
+ nsSplitterFrame* mOuter;
+ bool mDidDrag;
+ nscoord mDragStart;
+ nscoord mCurrentPos;
+ nsIFrame* mParentBox;
+ bool mPressed;
+ UniquePtr<nsSplitterInfo[]> mChildInfosBefore;
+ UniquePtr<nsSplitterInfo[]> mChildInfosAfter;
+ int32_t mChildInfosBeforeCount;
+ int32_t mChildInfosAfterCount;
+ State mState;
+ nscoord mSplitterPos;
+ bool mDragging;
+
+};
+
+NS_IMPL_ISUPPORTS(nsSplitterFrameInner, nsIDOMEventListener)
+
+nsSplitterFrameInner::ResizeType
+nsSplitterFrameInner::GetResizeBefore()
+{
+ static nsIContent::AttrValuesArray strings[] =
+ {&nsGkAtoms::farthest, &nsGkAtoms::flex, nullptr};
+ switch (mOuter->GetContent()->FindAttrValueIn(kNameSpaceID_None,
+ nsGkAtoms::resizebefore,
+ strings, eCaseMatters)) {
+ case 0: return Farthest;
+ case 1: return Flex;
+ }
+ return Closest;
+}
+
+nsSplitterFrameInner::~nsSplitterFrameInner()
+{
+}
+
+nsSplitterFrameInner::ResizeType
+nsSplitterFrameInner::GetResizeAfter()
+{
+ static nsIContent::AttrValuesArray strings[] =
+ {&nsGkAtoms::farthest, &nsGkAtoms::flex, &nsGkAtoms::grow, nullptr};
+ switch (mOuter->GetContent()->FindAttrValueIn(kNameSpaceID_None,
+ nsGkAtoms::resizeafter,
+ strings, eCaseMatters)) {
+ case 0: return Farthest;
+ case 1: return Flex;
+ case 2: return Grow;
+ }
+ return Closest;
+}
+
+nsSplitterFrameInner::State
+nsSplitterFrameInner::GetState()
+{
+ static nsIContent::AttrValuesArray strings[] =
+ {&nsGkAtoms::dragging, &nsGkAtoms::collapsed, nullptr};
+ static nsIContent::AttrValuesArray strings_substate[] =
+ {&nsGkAtoms::before, &nsGkAtoms::after, nullptr};
+ switch (mOuter->GetContent()->FindAttrValueIn(kNameSpaceID_None,
+ nsGkAtoms::state,
+ strings, eCaseMatters)) {
+ case 0: return Dragging;
+ case 1:
+ switch (mOuter->GetContent()->FindAttrValueIn(kNameSpaceID_None,
+ nsGkAtoms::substate,
+ strings_substate,
+ eCaseMatters)) {
+ case 0: return CollapsedBefore;
+ case 1: return CollapsedAfter;
+ default:
+ if (SupportsCollapseDirection(After))
+ return CollapsedAfter;
+ return CollapsedBefore;
+ }
+ }
+ return Open;
+}
+
+//
+// NS_NewSplitterFrame
+//
+// Creates a new Toolbar frame and returns it
+//
+nsIFrame*
+NS_NewSplitterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) nsSplitterFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsSplitterFrame)
+
+nsSplitterFrame::nsSplitterFrame(nsStyleContext* aContext)
+: nsBoxFrame(aContext),
+ mInner(0)
+{
+}
+
+void
+nsSplitterFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+ if (mInner) {
+ mInner->RemoveListener();
+ mInner->Disconnect();
+ mInner->Release();
+ mInner = nullptr;
+ }
+ nsBoxFrame::DestroyFrom(aDestructRoot);
+}
+
+
+nsresult
+nsSplitterFrame::GetCursor(const nsPoint& aPoint,
+ nsIFrame::Cursor& aCursor)
+{
+ return nsBoxFrame::GetCursor(aPoint, aCursor);
+
+ /*
+ if (IsXULHorizontal())
+ aCursor = NS_STYLE_CURSOR_N_RESIZE;
+ else
+ aCursor = NS_STYLE_CURSOR_W_RESIZE;
+
+ return NS_OK;
+ */
+}
+
+nsresult
+nsSplitterFrame::AttributeChanged(int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType)
+{
+ nsresult rv = nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute,
+ aModType);
+ // if the alignment changed. Let the grippy know
+ if (aAttribute == nsGkAtoms::align) {
+ // tell the slider its attribute changed so it can
+ // update itself
+ nsIFrame* grippy = nullptr;
+ nsScrollbarButtonFrame::GetChildWithTag(nsGkAtoms::grippy, this, grippy);
+ if (grippy)
+ grippy->AttributeChanged(aNameSpaceID, aAttribute, aModType);
+ } else if (aAttribute == nsGkAtoms::state) {
+ mInner->UpdateState();
+ }
+
+ return rv;
+}
+
+/**
+ * Initialize us. If we are in a box get our alignment so we know what direction we are
+ */
+void
+nsSplitterFrame::Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow)
+{
+ MOZ_ASSERT(!mInner);
+ mInner = new nsSplitterFrameInner(this);
+
+ mInner->AddRef();
+ mInner->mState = nsSplitterFrameInner::Open;
+ mInner->mDragging = false;
+
+ // determine orientation of parent, and if vertical, set orient to vertical
+ // on splitter content, then re-resolve style
+ // XXXbz this is pretty messed up, since this can change whether we should
+ // have a frame at all. This really needs a better solution.
+ if (aParent && aParent->IsXULBoxFrame()) {
+ if (!aParent->IsXULHorizontal()) {
+ if (!nsContentUtils::HasNonEmptyAttr(aContent, kNameSpaceID_None,
+ nsGkAtoms::orient)) {
+ aContent->SetAttr(kNameSpaceID_None, nsGkAtoms::orient,
+ NS_LITERAL_STRING("vertical"), false);
+ nsStyleContext* parentStyleContext = StyleContext()->GetParent();
+ RefPtr<nsStyleContext> newContext = PresContext()->StyleSet()->
+ ResolveStyleFor(aContent->AsElement(), parentStyleContext);
+ SetStyleContextWithoutNotification(newContext);
+ }
+ }
+ }
+
+ nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
+
+ mInner->mState = nsSplitterFrameInner::Open;
+ mInner->AddListener();
+ mInner->mParentBox = nullptr;
+}
+
+NS_IMETHODIMP
+nsSplitterFrame::DoXULLayout(nsBoxLayoutState& aState)
+{
+ if (GetStateBits() & NS_FRAME_FIRST_REFLOW)
+ {
+ mInner->mParentBox = nsBox::GetParentXULBox(this);
+ mInner->UpdateState();
+ }
+
+ return nsBoxFrame::DoXULLayout(aState);
+}
+
+
+void
+nsSplitterFrame::GetInitialOrientation(bool& aIsHorizontal)
+{
+ nsIFrame* box = nsBox::GetParentXULBox(this);
+ if (box) {
+ aIsHorizontal = !box->IsXULHorizontal();
+ }
+ else
+ nsBoxFrame::GetInitialOrientation(aIsHorizontal);
+}
+
+NS_IMETHODIMP
+nsSplitterFrame::HandlePress(nsPresContext* aPresContext,
+ WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSplitterFrame::HandleMultiplePress(nsPresContext* aPresContext,
+ WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus,
+ bool aControlHeld)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSplitterFrame::HandleDrag(nsPresContext* aPresContext,
+ WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSplitterFrame::HandleRelease(nsPresContext* aPresContext,
+ WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus)
+{
+ return NS_OK;
+}
+
+void
+nsSplitterFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ nsBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
+
+ // if the mouse is captured always return us as the frame.
+ if (mInner->mDragging)
+ {
+ // XXX It's probably better not to check visibility here, right?
+ aLists.Outlines()->AppendNewToTop(new (aBuilder)
+ nsDisplayEventReceiver(aBuilder, this));
+ return;
+ }
+}
+
+nsresult
+nsSplitterFrame::HandleEvent(nsPresContext* aPresContext,
+ WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus)
+{
+ NS_ENSURE_ARG_POINTER(aEventStatus);
+ if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
+ return NS_OK;
+ }
+
+ nsWeakFrame weakFrame(this);
+ RefPtr<nsSplitterFrameInner> inner(mInner);
+ switch (aEvent->mMessage) {
+ case eMouseMove:
+ inner->MouseDrag(aPresContext, aEvent);
+ break;
+
+ case eMouseUp:
+ if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
+ inner->MouseUp(aPresContext, aEvent);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ NS_ENSURE_STATE(weakFrame.IsAlive());
+ return nsBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
+}
+
+void
+nsSplitterFrameInner::MouseUp(nsPresContext* aPresContext,
+ WidgetGUIEvent* aEvent)
+{
+ if (mDragging && mOuter) {
+ AdjustChildren(aPresContext);
+ AddListener();
+ nsIPresShell::SetCapturingContent(nullptr, 0); // XXXndeakin is this needed?
+ mDragging = false;
+ State newState = GetState();
+ // if the state is dragging then make it Open.
+ if (newState == Dragging)
+ mOuter->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::state, EmptyString(), true);
+
+ mPressed = false;
+
+ // if we dragged then fire a command event.
+ if (mDidDrag) {
+ nsCOMPtr<nsIDOMXULElement> element = do_QueryInterface(mOuter->GetContent());
+ element->DoCommand();
+ }
+
+ //printf("MouseUp\n");
+ }
+
+ mChildInfosBefore = nullptr;
+ mChildInfosAfter = nullptr;
+ mChildInfosBeforeCount = 0;
+ mChildInfosAfterCount = 0;
+}
+
+void
+nsSplitterFrameInner::MouseDrag(nsPresContext* aPresContext,
+ WidgetGUIEvent* aEvent)
+{
+ if (mDragging && mOuter) {
+
+ //printf("Dragging\n");
+
+ bool isHorizontal = !mOuter->IsXULHorizontal();
+ // convert coord to pixels
+ nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent,
+ mParentBox);
+ nscoord pos = isHorizontal ? pt.x : pt.y;
+
+ // mDragStart is in frame coordinates
+ nscoord start = mDragStart;
+
+ // take our current position and subtract the start location
+ pos -= start;
+
+ //printf("Diff=%d\n", pos);
+
+ ResizeType resizeAfter = GetResizeAfter();
+
+ bool bounded;
+
+ if (resizeAfter == nsSplitterFrameInner::Grow)
+ bounded = false;
+ else
+ bounded = true;
+
+ int i;
+ for (i=0; i < mChildInfosBeforeCount; i++)
+ mChildInfosBefore[i].changed = mChildInfosBefore[i].current;
+
+ for (i=0; i < mChildInfosAfterCount; i++)
+ mChildInfosAfter[i].changed = mChildInfosAfter[i].current;
+
+ nscoord oldPos = pos;
+
+ ResizeChildTo(pos,
+ mChildInfosBefore.get(), mChildInfosAfter.get(),
+ mChildInfosBeforeCount, mChildInfosAfterCount, bounded);
+
+ State currentState = GetState();
+ bool supportsBefore = SupportsCollapseDirection(Before);
+ bool supportsAfter = SupportsCollapseDirection(After);
+
+ const bool isRTL = mOuter->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
+ bool pastEnd = oldPos > 0 && oldPos > pos;
+ bool pastBegin = oldPos < 0 && oldPos < pos;
+ if (isRTL) {
+ // Swap the boundary checks in RTL mode
+ bool tmp = pastEnd;
+ pastEnd = pastBegin;
+ pastBegin = tmp;
+ }
+ const bool isCollapsedBefore = pastBegin && supportsBefore;
+ const bool isCollapsedAfter = pastEnd && supportsAfter;
+
+ // if we are in a collapsed position
+ if (isCollapsedBefore || isCollapsedAfter)
+ {
+ // and we are not collapsed then collapse
+ if (currentState == Dragging) {
+ if (pastEnd)
+ {
+ //printf("Collapse right\n");
+ if (supportsAfter)
+ {
+ nsCOMPtr<nsIContent> outer = mOuter->mContent;
+ outer->SetAttr(kNameSpaceID_None, nsGkAtoms::substate,
+ NS_LITERAL_STRING("after"),
+ true);
+ outer->SetAttr(kNameSpaceID_None, nsGkAtoms::state,
+ NS_LITERAL_STRING("collapsed"),
+ true);
+ }
+
+ } else if (pastBegin)
+ {
+ //printf("Collapse left\n");
+ if (supportsBefore)
+ {
+ nsCOMPtr<nsIContent> outer = mOuter->mContent;
+ outer->SetAttr(kNameSpaceID_None, nsGkAtoms::substate,
+ NS_LITERAL_STRING("before"),
+ true);
+ outer->SetAttr(kNameSpaceID_None, nsGkAtoms::state,
+ NS_LITERAL_STRING("collapsed"),
+ true);
+ }
+ }
+ }
+ } else {
+ // if we are not in a collapsed position and we are not dragging make sure
+ // we are dragging.
+ if (currentState != Dragging)
+ mOuter->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::state, NS_LITERAL_STRING("dragging"), true);
+ AdjustChildren(aPresContext);
+ }
+
+ mDidDrag = true;
+ }
+}
+
+void
+nsSplitterFrameInner::AddListener()
+{
+ mOuter->GetContent()->
+ AddEventListener(NS_LITERAL_STRING("mouseup"), this, false, false);
+ mOuter->GetContent()->
+ AddEventListener(NS_LITERAL_STRING("mousedown"), this, false, false);
+ mOuter->GetContent()->
+ AddEventListener(NS_LITERAL_STRING("mousemove"), this, false, false);
+ mOuter->GetContent()->
+ AddEventListener(NS_LITERAL_STRING("mouseout"), this, false, false);
+}
+
+void
+nsSplitterFrameInner::RemoveListener()
+{
+ ENSURE_TRUE(mOuter);
+ mOuter->GetContent()->
+ RemoveEventListener(NS_LITERAL_STRING("mouseup"), this, false);
+ mOuter->GetContent()->
+ RemoveEventListener(NS_LITERAL_STRING("mousedown"), this, false);
+ mOuter->GetContent()->
+ RemoveEventListener(NS_LITERAL_STRING("mousemove"), this, false);
+ mOuter->GetContent()->
+ RemoveEventListener(NS_LITERAL_STRING("mouseout"), this, false);
+}
+
+nsresult
+nsSplitterFrameInner::HandleEvent(nsIDOMEvent* aEvent)
+{
+ nsAutoString eventType;
+ aEvent->GetType(eventType);
+ if (eventType.EqualsLiteral("mouseup"))
+ return MouseUp(aEvent);
+ if (eventType.EqualsLiteral("mousedown"))
+ return MouseDown(aEvent);
+ if (eventType.EqualsLiteral("mousemove") ||
+ eventType.EqualsLiteral("mouseout"))
+ return MouseMove(aEvent);
+
+ NS_ABORT();
+ return NS_OK;
+}
+
+nsresult
+nsSplitterFrameInner::MouseUp(nsIDOMEvent* aMouseEvent)
+{
+ NS_ENSURE_TRUE(mOuter, NS_OK);
+ mPressed = false;
+
+ nsIPresShell::SetCapturingContent(nullptr, 0);
+
+ return NS_OK;
+}
+
+nsresult
+nsSplitterFrameInner::MouseDown(nsIDOMEvent* aMouseEvent)
+{
+ NS_ENSURE_TRUE(mOuter, NS_OK);
+ nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(aMouseEvent));
+ if (!mouseEvent)
+ return NS_OK;
+
+ int16_t button = 0;
+ mouseEvent->GetButton(&button);
+
+ // only if left button
+ if (button != 0)
+ return NS_OK;
+
+ if (mOuter->GetContent()->
+ AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
+ nsGkAtoms::_true, eCaseMatters))
+ return NS_OK;
+
+ mParentBox = nsBox::GetParentXULBox(mOuter);
+ if (!mParentBox)
+ return NS_OK;
+
+ // get our index
+ nsPresContext* outerPresContext = mOuter->PresContext();
+ const nsFrameList& siblingList(mParentBox->PrincipalChildList());
+ int32_t childIndex = siblingList.IndexOf(mOuter);
+ // if it's 0 (or not found) then stop right here.
+ // It might be not found if we're not in the parent's primary frame list.
+ if (childIndex <= 0)
+ return NS_OK;
+
+ int32_t childCount = siblingList.GetLength();
+ // if it's the last index then we need to allow for resizeafter="grow"
+ if (childIndex == childCount - 1 && GetResizeAfter() != Grow)
+ return NS_OK;
+
+ nsRenderingContext rc(
+ outerPresContext->PresShell()->CreateReferenceRenderingContext());
+ nsBoxLayoutState state(outerPresContext, &rc);
+ mCurrentPos = 0;
+ mPressed = true;
+
+ mDidDrag = false;
+
+ EnsureOrient();
+ bool isHorizontal = !mOuter->IsXULHorizontal();
+
+ ResizeType resizeBefore = GetResizeBefore();
+ ResizeType resizeAfter = GetResizeAfter();
+
+ mChildInfosBefore = MakeUnique<nsSplitterInfo[]>(childCount);
+ mChildInfosAfter = MakeUnique<nsSplitterInfo[]>(childCount);
+
+ // create info 2 lists. One of the children before us and one after.
+ int32_t count = 0;
+ mChildInfosBeforeCount = 0;
+ mChildInfosAfterCount = 0;
+
+ nsIFrame* childBox = nsBox::GetChildXULBox(mParentBox);
+
+ while (nullptr != childBox)
+ {
+ nsIContent* content = childBox->GetContent();
+ nsIDocument* doc = content->OwnerDoc();
+ int32_t dummy;
+ nsIAtom* atom = doc->BindingManager()->ResolveTag(content, &dummy);
+
+ // skip over any splitters
+ if (atom != nsGkAtoms::splitter) {
+ nsSize prefSize = childBox->GetXULPrefSize(state);
+ nsSize minSize = childBox->GetXULMinSize(state);
+ nsSize maxSize = nsBox::BoundsCheckMinMax(minSize, childBox->GetXULMaxSize(state));
+ prefSize = nsBox::BoundsCheck(minSize, prefSize, maxSize);
+
+ mOuter->AddMargin(childBox, minSize);
+ mOuter->AddMargin(childBox, prefSize);
+ mOuter->AddMargin(childBox, maxSize);
+
+ nscoord flex = childBox->GetXULFlex();
+
+ nsMargin margin(0,0,0,0);
+ childBox->GetXULMargin(margin);
+ nsRect r(childBox->GetRect());
+ r.Inflate(margin);
+
+ // We need to check for hidden attribute too, since treecols with
+ // the hidden="true" attribute are not really hidden, just collapsed
+ if (!content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::fixed,
+ nsGkAtoms::_true, eCaseMatters) &&
+ !content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
+ nsGkAtoms::_true, eCaseMatters)) {
+ if (count < childIndex && (resizeBefore != Flex || flex > 0)) {
+ mChildInfosBefore[mChildInfosBeforeCount].childElem = content;
+ mChildInfosBefore[mChildInfosBeforeCount].min = isHorizontal ? minSize.width : minSize.height;
+ mChildInfosBefore[mChildInfosBeforeCount].max = isHorizontal ? maxSize.width : maxSize.height;
+ mChildInfosBefore[mChildInfosBeforeCount].current = isHorizontal ? r.width : r.height;
+ mChildInfosBefore[mChildInfosBeforeCount].flex = flex;
+ mChildInfosBefore[mChildInfosBeforeCount].index = count;
+ mChildInfosBefore[mChildInfosBeforeCount].changed = mChildInfosBefore[mChildInfosBeforeCount].current;
+ mChildInfosBeforeCount++;
+ } else if (count > childIndex && (resizeAfter != Flex || flex > 0)) {
+ mChildInfosAfter[mChildInfosAfterCount].childElem = content;
+ mChildInfosAfter[mChildInfosAfterCount].min = isHorizontal ? minSize.width : minSize.height;
+ mChildInfosAfter[mChildInfosAfterCount].max = isHorizontal ? maxSize.width : maxSize.height;
+ mChildInfosAfter[mChildInfosAfterCount].current = isHorizontal ? r.width : r.height;
+ mChildInfosAfter[mChildInfosAfterCount].flex = flex;
+ mChildInfosAfter[mChildInfosAfterCount].index = count;
+ mChildInfosAfter[mChildInfosAfterCount].changed = mChildInfosAfter[mChildInfosAfterCount].current;
+ mChildInfosAfterCount++;
+ }
+ }
+ }
+
+ childBox = nsBox::GetNextXULBox(childBox);
+ count++;
+ }
+
+ if (!mParentBox->IsXULNormalDirection()) {
+ // The before array is really the after array, and the order needs to be reversed.
+ // First reverse both arrays.
+ Reverse(mChildInfosBefore, mChildInfosBeforeCount);
+ Reverse(mChildInfosAfter, mChildInfosAfterCount);
+
+ // Now swap the two arrays.
+ Swap(mChildInfosBeforeCount, mChildInfosAfterCount);
+ Swap(mChildInfosBefore, mChildInfosAfter);
+ }
+
+ // if resizebefore is not Farthest, reverse the list because the first child
+ // in the list is the farthest, and we want the first child to be the closest.
+ if (resizeBefore != Farthest)
+ Reverse(mChildInfosBefore, mChildInfosBeforeCount);
+
+ // if the resizeafter is the Farthest we must reverse the list because the first child in the list
+ // is the closest we want the first child to be the Farthest.
+ if (resizeAfter == Farthest)
+ Reverse(mChildInfosAfter, mChildInfosAfterCount);
+
+ // grow only applys to the children after. If grow is set then no space should be taken out of any children after
+ // us. To do this we just set the size of that list to be 0.
+ if (resizeAfter == Grow)
+ mChildInfosAfterCount = 0;
+
+ int32_t c;
+ nsPoint pt = nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(mouseEvent->AsEvent(),
+ mParentBox);
+ if (isHorizontal) {
+ c = pt.x;
+ mSplitterPos = mOuter->mRect.x;
+ } else {
+ c = pt.y;
+ mSplitterPos = mOuter->mRect.y;
+ }
+
+ mDragStart = c;
+
+ //printf("Pressed mDragStart=%d\n",mDragStart);
+
+ nsIPresShell::SetCapturingContent(mOuter->GetContent(), CAPTURE_IGNOREALLOWED);
+
+ return NS_OK;
+}
+
+nsresult
+nsSplitterFrameInner::MouseMove(nsIDOMEvent* aMouseEvent)
+{
+ NS_ENSURE_TRUE(mOuter, NS_OK);
+ if (!mPressed)
+ return NS_OK;
+
+ if (mDragging)
+ return NS_OK;
+
+ nsCOMPtr<nsIDOMEventListener> kungfuDeathGrip(this);
+ mOuter->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::state,
+ NS_LITERAL_STRING("dragging"), true);
+
+ RemoveListener();
+ mDragging = true;
+
+ return NS_OK;
+}
+
+void
+nsSplitterFrameInner::Reverse(UniquePtr<nsSplitterInfo[]>& aChildInfos, int32_t aCount)
+{
+ UniquePtr<nsSplitterInfo[]> infos(new nsSplitterInfo[aCount]);
+
+ for (int i=0; i < aCount; i++)
+ infos[i] = aChildInfos[aCount - 1 - i];
+
+ aChildInfos = Move(infos);
+}
+
+bool
+nsSplitterFrameInner::SupportsCollapseDirection
+(
+ nsSplitterFrameInner::CollapseDirection aDirection
+)
+{
+ static nsIContent::AttrValuesArray strings[] =
+ {&nsGkAtoms::before, &nsGkAtoms::after, &nsGkAtoms::both, nullptr};
+
+ switch (mOuter->mContent->FindAttrValueIn(kNameSpaceID_None,
+ nsGkAtoms::collapse,
+ strings, eCaseMatters)) {
+ case 0:
+ return (aDirection == Before);
+ case 1:
+ return (aDirection == After);
+ case 2:
+ return true;
+ }
+
+ return false;
+}
+
+void
+nsSplitterFrameInner::UpdateState()
+{
+ // State Transitions:
+ // Open -> Dragging
+ // Open -> CollapsedBefore
+ // Open -> CollapsedAfter
+ // CollapsedBefore -> Open
+ // CollapsedBefore -> Dragging
+ // CollapsedAfter -> Open
+ // CollapsedAfter -> Dragging
+ // Dragging -> Open
+ // Dragging -> CollapsedBefore (auto collapse)
+ // Dragging -> CollapsedAfter (auto collapse)
+
+ State newState = GetState();
+
+ if (newState == mState) {
+ // No change.
+ return;
+ }
+
+ if ((SupportsCollapseDirection(Before) || SupportsCollapseDirection(After)) &&
+ mOuter->GetParent()->IsXULBoxFrame()) {
+ // Find the splitter's immediate sibling.
+ nsIFrame* splitterSibling;
+ if (newState == CollapsedBefore || mState == CollapsedBefore) {
+ splitterSibling = mOuter->GetPrevSibling();
+ } else {
+ splitterSibling = mOuter->GetNextSibling();
+ }
+
+ if (splitterSibling) {
+ nsCOMPtr<nsIContent> sibling = splitterSibling->GetContent();
+ if (sibling) {
+ if (mState == CollapsedBefore || mState == CollapsedAfter) {
+ // CollapsedBefore -> Open
+ // CollapsedBefore -> Dragging
+ // CollapsedAfter -> Open
+ // CollapsedAfter -> Dragging
+ nsContentUtils::AddScriptRunner(
+ new nsUnsetAttrRunnable(sibling, nsGkAtoms::collapsed));
+ } else if ((mState == Open || mState == Dragging)
+ && (newState == CollapsedBefore ||
+ newState == CollapsedAfter)) {
+ // Open -> CollapsedBefore / CollapsedAfter
+ // Dragging -> CollapsedBefore / CollapsedAfter
+ nsContentUtils::AddScriptRunner(
+ new nsSetAttrRunnable(sibling, nsGkAtoms::collapsed,
+ NS_LITERAL_STRING("true")));
+ }
+ }
+ }
+ }
+ mState = newState;
+}
+
+void
+nsSplitterFrameInner::EnsureOrient()
+{
+ bool isHorizontal = !(mParentBox->GetStateBits() & NS_STATE_IS_HORIZONTAL);
+ if (isHorizontal)
+ mOuter->mState |= NS_STATE_IS_HORIZONTAL;
+ else
+ mOuter->mState &= ~NS_STATE_IS_HORIZONTAL;
+}
+
+void
+nsSplitterFrameInner::AdjustChildren(nsPresContext* aPresContext)
+{
+ EnsureOrient();
+ bool isHorizontal = !mOuter->IsXULHorizontal();
+
+ AdjustChildren(aPresContext, mChildInfosBefore.get(),
+ mChildInfosBeforeCount, isHorizontal);
+ AdjustChildren(aPresContext, mChildInfosAfter.get(),
+ mChildInfosAfterCount, isHorizontal);
+}
+
+static nsIFrame* GetChildBoxForContent(nsIFrame* aParentBox, nsIContent* aContent)
+{
+ nsIFrame* childBox = nsBox::GetChildXULBox(aParentBox);
+
+ while (nullptr != childBox) {
+ if (childBox->GetContent() == aContent) {
+ return childBox;
+ }
+ childBox = nsBox::GetNextXULBox(childBox);
+ }
+ return nullptr;
+}
+
+void
+nsSplitterFrameInner::AdjustChildren(nsPresContext* aPresContext, nsSplitterInfo* aChildInfos, int32_t aCount, bool aIsHorizontal)
+{
+ ///printf("------- AdjustChildren------\n");
+
+ nsBoxLayoutState state(aPresContext);
+
+ nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
+
+ // first set all the widths.
+ nsIFrame* child = nsBox::GetChildXULBox(mOuter);
+ while(child)
+ {
+ SetPreferredSize(state, child, onePixel, aIsHorizontal, nullptr);
+ child = nsBox::GetNextXULBox(child);
+ }
+
+ // now set our changed widths.
+ for (int i=0; i < aCount; i++)
+ {
+ nscoord pref = aChildInfos[i].changed;
+ nsIFrame* childBox = GetChildBoxForContent(mParentBox, aChildInfos[i].childElem);
+
+ if (childBox) {
+ SetPreferredSize(state, childBox, onePixel, aIsHorizontal, &pref);
+ }
+ }
+}
+
+void
+nsSplitterFrameInner::SetPreferredSize(nsBoxLayoutState& aState, nsIFrame* aChildBox, nscoord aOnePixel, bool aIsHorizontal, nscoord* aSize)
+{
+ nsRect rect(aChildBox->GetRect());
+ nscoord pref = 0;
+
+ if (!aSize)
+ {
+ if (aIsHorizontal)
+ pref = rect.width;
+ else
+ pref = rect.height;
+ } else {
+ pref = *aSize;
+ }
+
+ nsMargin margin(0,0,0,0);
+ aChildBox->GetXULMargin(margin);
+
+ nsCOMPtr<nsIAtom> attribute;
+
+ if (aIsHorizontal) {
+ pref -= (margin.left + margin.right);
+ attribute = nsGkAtoms::width;
+ } else {
+ pref -= (margin.top + margin.bottom);
+ attribute = nsGkAtoms::height;
+ }
+
+ nsIContent* content = aChildBox->GetContent();
+
+ // set its preferred size.
+ nsAutoString prefValue;
+ prefValue.AppendInt(pref/aOnePixel);
+ if (content->AttrValueIs(kNameSpaceID_None, attribute,
+ prefValue, eCaseMatters))
+ return;
+
+ nsWeakFrame weakBox(aChildBox);
+ content->SetAttr(kNameSpaceID_None, attribute, prefValue, true);
+ ENSURE_TRUE(weakBox.IsAlive());
+ aState.PresShell()->FrameNeedsReflow(aChildBox, nsIPresShell::eStyleChange,
+ NS_FRAME_IS_DIRTY);
+}
+
+
+void
+nsSplitterFrameInner::AddRemoveSpace(nscoord aDiff,
+ nsSplitterInfo* aChildInfos,
+ int32_t aCount,
+ int32_t& aSpaceLeft)
+{
+ aSpaceLeft = 0;
+
+ for (int i=0; i < aCount; i++) {
+ nscoord min = aChildInfos[i].min;
+ nscoord max = aChildInfos[i].max;
+ nscoord& c = aChildInfos[i].changed;
+
+ // figure our how much space to add or remove
+ if (c + aDiff < min) {
+ aDiff += (c - min);
+ c = min;
+ } else if (c + aDiff > max) {
+ aDiff -= (max - c);
+ c = max;
+ } else {
+ c += aDiff;
+ aDiff = 0;
+ }
+
+ // there is not space left? We are done
+ if (aDiff == 0)
+ break;
+ }
+
+ aSpaceLeft = aDiff;
+}
+
+/**
+ * Ok if we want to resize a child we will know the actual size in pixels we want it to be.
+ * This is not the preferred size. But they only way we can change a child is my manipulating its
+ * preferred size. So give the actual pixel size this return method will return figure out the preferred
+ * size and set it.
+ */
+
+void
+nsSplitterFrameInner::ResizeChildTo(nscoord& aDiff,
+ nsSplitterInfo* aChildrenBeforeInfos,
+ nsSplitterInfo* aChildrenAfterInfos,
+ int32_t aChildrenBeforeCount,
+ int32_t aChildrenAfterCount,
+ bool aBounded)
+{
+ nscoord spaceLeft;
+ AddRemoveSpace(aDiff, aChildrenBeforeInfos,aChildrenBeforeCount,spaceLeft);
+
+ // if there is any space left over remove it from the dif we were originally given
+ aDiff -= spaceLeft;
+ AddRemoveSpace(-aDiff, aChildrenAfterInfos,aChildrenAfterCount,spaceLeft);
+
+ if (spaceLeft != 0) {
+ if (aBounded) {
+ aDiff += spaceLeft;
+ AddRemoveSpace(spaceLeft, aChildrenBeforeInfos,aChildrenBeforeCount,spaceLeft);
+ }
+ }
+}