layout/xul/nsSplitterFrame.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 //
     7 // Eric Vaughan
     8 // Netscape Communications
     9 //
    10 // See documentation in associated header file
    11 //
    13 #include "nsSplitterFrame.h"
    14 #include "nsGkAtoms.h"
    15 #include "nsIDOMElement.h"
    16 #include "nsIDOMXULElement.h"
    17 #include "nsPresContext.h"
    18 #include "nsRenderingContext.h"
    19 #include "nsIDocument.h"
    20 #include "nsNameSpaceManager.h"
    21 #include "nsScrollbarButtonFrame.h"
    22 #include "nsIDOMEventListener.h"
    23 #include "nsIDOMMouseEvent.h"
    24 #include "nsIPresShell.h"
    25 #include "nsFrameList.h"
    26 #include "nsHTMLParts.h"
    27 #include "nsStyleContext.h"
    28 #include "nsBoxLayoutState.h"
    29 #include "nsIServiceManager.h"
    30 #include "nsContainerFrame.h"
    31 #include "nsAutoPtr.h"
    32 #include "nsContentCID.h"
    33 #include "nsStyleSet.h"
    34 #include "nsLayoutUtils.h"
    35 #include "nsDisplayList.h"
    36 #include "nsContentUtils.h"
    37 #include "mozilla/dom/Element.h"
    38 #include "mozilla/MouseEvents.h"
    40 using namespace mozilla;
    42 class nsSplitterInfo {
    43 public:
    44   nscoord min;
    45   nscoord max;
    46   nscoord current;
    47   nscoord changed;
    48   nsCOMPtr<nsIContent> childElem;
    49   int32_t flex;
    50   int32_t index;
    51 };
    53 class nsSplitterFrameInner : public nsIDOMEventListener
    54 {
    55 public:
    57   NS_DECL_ISUPPORTS
    58   NS_DECL_NSIDOMEVENTLISTENER
    60   nsSplitterFrameInner(nsSplitterFrame* aSplitter)
    61   {
    62     mOuter = aSplitter;
    63     mPressed = false;
    64   }
    65   virtual ~nsSplitterFrameInner();
    67   void Disconnect() { mOuter = nullptr; }
    69   nsresult MouseDown(nsIDOMEvent* aMouseEvent);
    70   nsresult MouseUp(nsIDOMEvent* aMouseEvent);
    71   nsresult MouseMove(nsIDOMEvent* aMouseEvent);
    73   void MouseDrag(nsPresContext* aPresContext, WidgetGUIEvent* aEvent);
    74   void MouseUp(nsPresContext* aPresContext, WidgetGUIEvent* aEvent);
    76   void AdjustChildren(nsPresContext* aPresContext);
    77   void AdjustChildren(nsPresContext* aPresContext, nsSplitterInfo* aChildInfos, int32_t aCount, bool aIsHorizontal);
    79   void AddRemoveSpace(nscoord aDiff,
    80                     nsSplitterInfo* aChildInfos,
    81                     int32_t aCount,
    82                     int32_t& aSpaceLeft);
    84   void ResizeChildTo(nsPresContext* aPresContext,
    85                    nscoord& aDiff, 
    86                    nsSplitterInfo* aChildrenBeforeInfos, 
    87                    nsSplitterInfo* aChildrenAfterInfos, 
    88                    int32_t aChildrenBeforeCount, 
    89                    int32_t aChildrenAfterCount, 
    90                    bool aBounded);
    92   void UpdateState();
    94   void AddListener(nsPresContext* aPresContext);
    95   void RemoveListener();
    97   enum ResizeType { Closest, Farthest, Flex, Grow };
    98   enum State { Open, CollapsedBefore, CollapsedAfter, Dragging };
    99   enum CollapseDirection { Before, After };
   101   ResizeType GetResizeBefore();
   102   ResizeType GetResizeAfter();
   103   State GetState();
   105   void Reverse(nsSplitterInfo*& aIndexes, int32_t aCount);
   106   bool SupportsCollapseDirection(CollapseDirection aDirection);
   108   void EnsureOrient();
   109   void SetPreferredSize(nsBoxLayoutState& aState, nsIFrame* aChildBox, nscoord aOnePixel, bool aIsHorizontal, nscoord* aSize);
   111   nsSplitterFrame* mOuter;
   112   bool mDidDrag;
   113   nscoord mDragStart;
   114   nscoord mCurrentPos;
   115   nsIFrame* mParentBox;
   116   bool mPressed;
   117   nsSplitterInfo* mChildInfosBefore;
   118   nsSplitterInfo* mChildInfosAfter;
   119   int32_t mChildInfosBeforeCount;
   120   int32_t mChildInfosAfterCount;
   121   State mState;
   122   nscoord mSplitterPos;
   123   bool mDragging;
   125 };
   127 NS_IMPL_ISUPPORTS(nsSplitterFrameInner, nsIDOMEventListener)
   129 nsSplitterFrameInner::ResizeType
   130 nsSplitterFrameInner::GetResizeBefore()
   131 {
   132   static nsIContent::AttrValuesArray strings[] =
   133     {&nsGkAtoms::farthest, &nsGkAtoms::flex, nullptr};
   134   switch (mOuter->GetContent()->FindAttrValueIn(kNameSpaceID_None,
   135                                                 nsGkAtoms::resizebefore,
   136                                                 strings, eCaseMatters)) {
   137     case 0: return Farthest;
   138     case 1: return Flex;
   139   }
   140   return Closest;
   141 }
   143 nsSplitterFrameInner::~nsSplitterFrameInner() 
   144 {
   145   delete[] mChildInfosBefore;
   146   delete[] mChildInfosAfter;
   147 }
   149 nsSplitterFrameInner::ResizeType
   150 nsSplitterFrameInner::GetResizeAfter()
   151 {
   152   static nsIContent::AttrValuesArray strings[] =
   153     {&nsGkAtoms::farthest, &nsGkAtoms::flex, &nsGkAtoms::grow, nullptr};
   154   switch (mOuter->GetContent()->FindAttrValueIn(kNameSpaceID_None,
   155                                                 nsGkAtoms::resizeafter,
   156                                                 strings, eCaseMatters)) {
   157     case 0: return Farthest;
   158     case 1: return Flex;
   159     case 2: return Grow;
   160   }
   161   return Closest;
   162 }
   164 nsSplitterFrameInner::State
   165 nsSplitterFrameInner::GetState()
   166 {
   167   static nsIContent::AttrValuesArray strings[] =
   168     {&nsGkAtoms::dragging, &nsGkAtoms::collapsed, nullptr};
   169   static nsIContent::AttrValuesArray strings_substate[] =
   170     {&nsGkAtoms::before, &nsGkAtoms::after, nullptr};
   171   switch (mOuter->GetContent()->FindAttrValueIn(kNameSpaceID_None,
   172                                                 nsGkAtoms::state,
   173                                                 strings, eCaseMatters)) {
   174     case 0: return Dragging;
   175     case 1:
   176       switch (mOuter->GetContent()->FindAttrValueIn(kNameSpaceID_None,
   177                                                     nsGkAtoms::substate,
   178                                                     strings_substate,
   179                                                     eCaseMatters)) {
   180         case 0: return CollapsedBefore;
   181         case 1: return CollapsedAfter;
   182         default:
   183           if (SupportsCollapseDirection(After))
   184             return CollapsedAfter;
   185           return CollapsedBefore;
   186       }
   187   }
   188   return Open;
   189 }
   191 //
   192 // NS_NewSplitterFrame
   193 //
   194 // Creates a new Toolbar frame and returns it
   195 //
   196 nsIFrame*
   197 NS_NewSplitterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext)
   198 {
   199   return new (aPresShell) nsSplitterFrame(aPresShell, aContext);
   200 }
   202 NS_IMPL_FRAMEARENA_HELPERS(nsSplitterFrame)
   204 nsSplitterFrame::nsSplitterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
   205 : nsBoxFrame(aPresShell, aContext),
   206   mInner(0)
   207 {
   208 }
   210 void
   211 nsSplitterFrame::DestroyFrom(nsIFrame* aDestructRoot)
   212 {
   213   if (mInner) {
   214     mInner->RemoveListener();
   215     mInner->Disconnect();
   216     mInner->Release();
   217     mInner = nullptr;
   218   }
   219   nsBoxFrame::DestroyFrom(aDestructRoot);
   220 }
   223 nsresult
   224 nsSplitterFrame::GetCursor(const nsPoint&    aPoint,
   225                            nsIFrame::Cursor& aCursor)
   226 {
   227   return nsBoxFrame::GetCursor(aPoint, aCursor);
   229   /*
   230     if (IsHorizontal())
   231       aCursor = NS_STYLE_CURSOR_N_RESIZE;
   232     else
   233       aCursor = NS_STYLE_CURSOR_W_RESIZE;
   235     return NS_OK;
   236   */
   237 }
   239 nsresult
   240 nsSplitterFrame::AttributeChanged(int32_t aNameSpaceID,
   241                                   nsIAtom* aAttribute,
   242                                   int32_t aModType)
   243 {
   244   nsresult rv = nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute,
   245                                              aModType);
   246   // if the alignment changed. Let the grippy know
   247   if (aAttribute == nsGkAtoms::align) {
   248     // tell the slider its attribute changed so it can 
   249     // update itself
   250     nsIFrame* grippy = nullptr;
   251     nsScrollbarButtonFrame::GetChildWithTag(PresContext(), nsGkAtoms::grippy, this, grippy);
   252     if (grippy)
   253       grippy->AttributeChanged(aNameSpaceID, aAttribute, aModType);
   254   } else if (aAttribute == nsGkAtoms::state) {
   255     mInner->UpdateState();
   256   }
   258   return rv;
   259 }
   261 /**
   262  * Initialize us. If we are in a box get our alignment so we know what direction we are
   263  */
   264 void
   265 nsSplitterFrame::Init(nsIContent*      aContent,
   266                       nsIFrame*        aParent,
   267                       nsIFrame*        aPrevInFlow)
   268 {
   269   MOZ_ASSERT(!mInner);
   270   mInner = new nsSplitterFrameInner(this);
   272   mInner->AddRef();
   273   mInner->mChildInfosAfter = nullptr;
   274   mInner->mChildInfosBefore = nullptr;
   275   mInner->mState = nsSplitterFrameInner::Open;
   276   mInner->mDragging = false;
   278   // determine orientation of parent, and if vertical, set orient to vertical
   279   // on splitter content, then re-resolve style
   280   // XXXbz this is pretty messed up, since this can change whether we should
   281   // have a frame at all.  This really needs a better solution.
   282   if (aParent && aParent->IsBoxFrame()) {
   283     if (!aParent->IsHorizontal()) {
   284       if (!nsContentUtils::HasNonEmptyAttr(aContent, kNameSpaceID_None,
   285                                            nsGkAtoms::orient)) {
   286         aContent->SetAttr(kNameSpaceID_None, nsGkAtoms::orient,
   287                           NS_LITERAL_STRING("vertical"), false);
   288         nsStyleContext* parentStyleContext = StyleContext()->GetParent();
   289         nsRefPtr<nsStyleContext> newContext = PresContext()->StyleSet()->
   290           ResolveStyleFor(aContent->AsElement(), parentStyleContext);
   291         SetStyleContextWithoutNotification(newContext);
   292       }
   293     }
   294   }
   296   nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
   298   mInner->mState = nsSplitterFrameInner::Open;
   299   mInner->AddListener(PresContext());
   300   mInner->mParentBox = nullptr;
   301 }
   303 NS_IMETHODIMP
   304 nsSplitterFrame::DoLayout(nsBoxLayoutState& aState)
   305 {
   306   if (GetStateBits() & NS_FRAME_FIRST_REFLOW) 
   307   {
   308     mInner->mParentBox = GetParentBox();
   309     mInner->UpdateState();
   310   }
   312   return nsBoxFrame::DoLayout(aState);
   313 }
   316 void
   317 nsSplitterFrame::GetInitialOrientation(bool& aIsHorizontal)
   318 {
   319   nsIFrame* box = GetParentBox();
   320   if (box) {
   321     aIsHorizontal = !box->IsHorizontal();
   322   }
   323   else
   324     nsBoxFrame::GetInitialOrientation(aIsHorizontal);
   325 }
   327 NS_IMETHODIMP
   328 nsSplitterFrame::HandlePress(nsPresContext* aPresContext,
   329                              WidgetGUIEvent* aEvent,
   330                              nsEventStatus* aEventStatus)
   331 {
   332   return NS_OK;
   333 }
   335 NS_IMETHODIMP
   336 nsSplitterFrame::HandleMultiplePress(nsPresContext* aPresContext,
   337                                      WidgetGUIEvent* aEvent,
   338                                      nsEventStatus* aEventStatus,
   339                                      bool aControlHeld)
   340 {
   341   return NS_OK;
   342 }
   344 NS_IMETHODIMP
   345 nsSplitterFrame::HandleDrag(nsPresContext* aPresContext,
   346                             WidgetGUIEvent* aEvent,
   347                             nsEventStatus* aEventStatus)
   348 {
   349   return NS_OK;
   350 }
   352 NS_IMETHODIMP
   353 nsSplitterFrame::HandleRelease(nsPresContext* aPresContext,
   354                                WidgetGUIEvent* aEvent,
   355                                nsEventStatus* aEventStatus)
   356 {
   357   return NS_OK;
   358 }
   360 void
   361 nsSplitterFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
   362                                   const nsRect&           aDirtyRect,
   363                                   const nsDisplayListSet& aLists)
   364 {
   365   nsBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
   367   // if the mouse is captured always return us as the frame.
   368   if (mInner->mDragging)
   369   {
   370     // XXX It's probably better not to check visibility here, right?
   371     aLists.Outlines()->AppendNewToTop(new (aBuilder)
   372       nsDisplayEventReceiver(aBuilder, this));
   373     return;
   374   }
   375 }
   377 nsresult
   378 nsSplitterFrame::HandleEvent(nsPresContext* aPresContext,
   379                              WidgetGUIEvent* aEvent,
   380                              nsEventStatus* aEventStatus)
   381 {
   382   NS_ENSURE_ARG_POINTER(aEventStatus);
   383   if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
   384     return NS_OK;
   385   }
   387   nsWeakFrame weakFrame(this);
   388   nsRefPtr<nsSplitterFrameInner> kungFuDeathGrip(mInner);
   389   switch (aEvent->message) {
   390     case NS_MOUSE_MOVE: 
   391       mInner->MouseDrag(aPresContext, aEvent);
   392     break;
   394     case NS_MOUSE_BUTTON_UP:
   395       if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
   396         mInner->MouseUp(aPresContext, aEvent);
   397       }
   398     break;
   399   }
   401   NS_ENSURE_STATE(weakFrame.IsAlive());
   402   return nsBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
   403 }
   405 void
   406 nsSplitterFrameInner::MouseUp(nsPresContext* aPresContext,
   407                               WidgetGUIEvent* aEvent)
   408 {
   409   if (mDragging && mOuter) {
   410     AdjustChildren(aPresContext);
   411     AddListener(aPresContext);
   412     nsIPresShell::SetCapturingContent(nullptr, 0); // XXXndeakin is this needed?
   413     mDragging = false;
   414     State newState = GetState(); 
   415     // if the state is dragging then make it Open.
   416     if (newState == Dragging)
   417       mOuter->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::state, EmptyString(), true);
   419     mPressed = false;
   421     // if we dragged then fire a command event.
   422     if (mDidDrag) {
   423       nsCOMPtr<nsIDOMXULElement> element = do_QueryInterface(mOuter->GetContent());
   424       element->DoCommand();
   425     }
   427     //printf("MouseUp\n");
   428   }
   430   delete[] mChildInfosBefore;
   431   delete[] mChildInfosAfter;
   432   mChildInfosBefore = nullptr;
   433   mChildInfosAfter = nullptr;
   434   mChildInfosBeforeCount = 0;
   435   mChildInfosAfterCount = 0;
   436 }
   438 void
   439 nsSplitterFrameInner::MouseDrag(nsPresContext* aPresContext,
   440                                 WidgetGUIEvent* aEvent)
   441 {
   442   if (mDragging && mOuter) {
   444     //printf("Dragging\n");
   446     bool isHorizontal = !mOuter->IsHorizontal();
   447     // convert coord to pixels
   448     nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent,
   449                                                               mParentBox);
   450     nscoord pos = isHorizontal ? pt.x : pt.y;
   452     // mDragStart is in frame coordinates
   453     nscoord start = mDragStart;
   455     // take our current position and subtract the start location
   456     pos -= start;
   458     //printf("Diff=%d\n", pos);
   460     ResizeType resizeAfter  = GetResizeAfter();
   462     bool bounded;
   464     if (resizeAfter == nsSplitterFrameInner::Grow)
   465       bounded = false;
   466     else 
   467       bounded = true;
   469     int i;
   470     for (i=0; i < mChildInfosBeforeCount; i++) 
   471       mChildInfosBefore[i].changed = mChildInfosBefore[i].current;
   473     for (i=0; i < mChildInfosAfterCount; i++) 
   474       mChildInfosAfter[i].changed = mChildInfosAfter[i].current;
   476     nscoord oldPos = pos;
   478     ResizeChildTo(aPresContext, pos, mChildInfosBefore, mChildInfosAfter, mChildInfosBeforeCount, mChildInfosAfterCount, bounded);
   480     State currentState = GetState();
   481     bool supportsBefore = SupportsCollapseDirection(Before);
   482     bool supportsAfter = SupportsCollapseDirection(After);
   484     const bool isRTL = mOuter->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
   485     bool pastEnd = oldPos > 0 && oldPos > pos;
   486     bool pastBegin = oldPos < 0 && oldPos < pos;
   487     if (isRTL) {
   488       // Swap the boundary checks in RTL mode
   489       bool tmp = pastEnd;
   490       pastEnd = pastBegin;
   491       pastBegin = tmp;
   492     }
   493     const bool isCollapsedBefore = pastBegin && supportsBefore;
   494     const bool isCollapsedAfter = pastEnd && supportsAfter;
   496     // if we are in a collapsed position
   497     if (isCollapsedBefore || isCollapsedAfter)
   498     {
   499       // and we are not collapsed then collapse
   500       if (currentState == Dragging) {
   501         if (pastEnd)
   502         {
   503           //printf("Collapse right\n");
   504           if (supportsAfter) 
   505           {
   506             nsCOMPtr<nsIContent> outer = mOuter->mContent;
   507             outer->SetAttr(kNameSpaceID_None, nsGkAtoms::substate,
   508                            NS_LITERAL_STRING("after"),
   509                            true);
   510             outer->SetAttr(kNameSpaceID_None, nsGkAtoms::state,
   511                            NS_LITERAL_STRING("collapsed"),
   512                            true);
   513           }
   515         } else if (pastBegin)
   516         {
   517           //printf("Collapse left\n");
   518           if (supportsBefore)
   519           {
   520             nsCOMPtr<nsIContent> outer = mOuter->mContent;
   521             outer->SetAttr(kNameSpaceID_None, nsGkAtoms::substate,
   522                            NS_LITERAL_STRING("before"),
   523                            true);
   524             outer->SetAttr(kNameSpaceID_None, nsGkAtoms::state,
   525                            NS_LITERAL_STRING("collapsed"),
   526                            true);
   527           }
   528         }
   529       }
   530     } else {
   531       // if we are not in a collapsed position and we are not dragging make sure
   532       // we are dragging.
   533       if (currentState != Dragging)
   534         mOuter->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::state, NS_LITERAL_STRING("dragging"), true);
   535       AdjustChildren(aPresContext);
   536     }
   538     mDidDrag = true;
   539   }
   540 }
   542 void
   543 nsSplitterFrameInner::AddListener(nsPresContext* aPresContext)
   544 {
   545   mOuter->GetContent()->
   546     AddEventListener(NS_LITERAL_STRING("mouseup"), this, false, false);
   547   mOuter->GetContent()->
   548     AddEventListener(NS_LITERAL_STRING("mousedown"), this, false, false);
   549   mOuter->GetContent()->
   550     AddEventListener(NS_LITERAL_STRING("mousemove"), this, false, false);
   551   mOuter->GetContent()->
   552     AddEventListener(NS_LITERAL_STRING("mouseout"), this, false, false);
   553 }
   555 void
   556 nsSplitterFrameInner::RemoveListener()
   557 {
   558   ENSURE_TRUE(mOuter);
   559   mOuter->GetContent()->
   560     RemoveEventListener(NS_LITERAL_STRING("mouseup"), this, false);
   561   mOuter->GetContent()->
   562     RemoveEventListener(NS_LITERAL_STRING("mousedown"), this, false);
   563   mOuter->GetContent()->
   564     RemoveEventListener(NS_LITERAL_STRING("mousemove"), this, false);
   565   mOuter->GetContent()->
   566     RemoveEventListener(NS_LITERAL_STRING("mouseout"), this, false);
   567 }
   569 nsresult
   570 nsSplitterFrameInner::HandleEvent(nsIDOMEvent* aEvent)
   571 {
   572   nsAutoString eventType;
   573   aEvent->GetType(eventType);
   574   if (eventType.EqualsLiteral("mouseup"))
   575     return MouseUp(aEvent);
   576   if (eventType.EqualsLiteral("mousedown"))
   577     return MouseDown(aEvent);
   578   if (eventType.EqualsLiteral("mousemove") ||
   579       eventType.EqualsLiteral("mouseout"))
   580     return MouseMove(aEvent);
   582   NS_ABORT();
   583   return NS_OK;
   584 }
   586 nsresult
   587 nsSplitterFrameInner::MouseUp(nsIDOMEvent* aMouseEvent)
   588 {  
   589   NS_ENSURE_TRUE(mOuter, NS_OK);
   590   mPressed = false;
   592   nsIPresShell::SetCapturingContent(nullptr, 0);
   594   return NS_OK;
   595 }
   597 nsresult
   598 nsSplitterFrameInner::MouseDown(nsIDOMEvent* aMouseEvent)
   599 {  
   600   NS_ENSURE_TRUE(mOuter, NS_OK);
   601   nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(aMouseEvent));
   602   if (!mouseEvent)
   603     return NS_OK;
   605   int16_t button = 0;
   606   mouseEvent->GetButton(&button);
   608   // only if left button
   609   if (button != 0)
   610      return NS_OK;
   612   if (mOuter->GetContent()->
   613         AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
   614                     nsGkAtoms::_true, eCaseMatters))
   615     return NS_OK;
   617   mParentBox = mOuter->GetParentBox();
   618   if (!mParentBox)
   619     return NS_OK;
   621   // get our index
   622   nsPresContext* outerPresContext = mOuter->PresContext();
   623   const nsFrameList& siblingList(mParentBox->PrincipalChildList());
   624   int32_t childIndex = siblingList.IndexOf(mOuter);
   625   // if it's 0 (or not found) then stop right here.
   626   // It might be not found if we're not in the parent's primary frame list.
   627   if (childIndex <= 0)
   628     return NS_OK;
   630   int32_t childCount = siblingList.GetLength();
   631   // if it's the last index then we need to allow for resizeafter="grow"
   632   if (childIndex == childCount - 1 && GetResizeAfter() != Grow)
   633     return NS_OK;
   635   nsRefPtr<nsRenderingContext> rc =
   636     outerPresContext->PresShell()->CreateReferenceRenderingContext();
   637   nsBoxLayoutState state(outerPresContext, rc);
   638   mCurrentPos = 0;
   639   mPressed = true;
   641   mDidDrag = false;
   643   EnsureOrient();
   644   bool isHorizontal = !mOuter->IsHorizontal();
   646   ResizeType resizeBefore = GetResizeBefore();
   647   ResizeType resizeAfter  = GetResizeAfter();
   649   delete[] mChildInfosBefore;
   650   delete[] mChildInfosAfter;
   651   mChildInfosBefore = new nsSplitterInfo[childCount];
   652   mChildInfosAfter  = new nsSplitterInfo[childCount];
   654   // create info 2 lists. One of the children before us and one after.
   655   int32_t count = 0;
   656   mChildInfosBeforeCount = 0;
   657   mChildInfosAfterCount = 0;
   659   nsIFrame* childBox = mParentBox->GetChildBox();
   661   while (nullptr != childBox) 
   662   { 
   663     nsIContent* content = childBox->GetContent();
   664     nsIDocument* doc = content->OwnerDoc();
   665     int32_t dummy;
   666     nsIAtom* atom = doc->BindingManager()->ResolveTag(content, &dummy);
   668     // skip over any splitters
   669     if (atom != nsGkAtoms::splitter) { 
   670         nsSize prefSize = childBox->GetPrefSize(state);
   671         nsSize minSize = childBox->GetMinSize(state);
   672         nsSize maxSize = nsBox::BoundsCheckMinMax(minSize, childBox->GetMaxSize(state));
   673         prefSize = nsBox::BoundsCheck(minSize, prefSize, maxSize);
   675         mOuter->AddMargin(childBox, minSize);
   676         mOuter->AddMargin(childBox, prefSize);
   677         mOuter->AddMargin(childBox, maxSize);
   679         nscoord flex = childBox->GetFlex(state);
   681         nsMargin margin(0,0,0,0);
   682         childBox->GetMargin(margin);
   683         nsRect r(childBox->GetRect());
   684         r.Inflate(margin);
   686         // We need to check for hidden attribute too, since treecols with
   687         // the hidden="true" attribute are not really hidden, just collapsed
   688         if (!content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::fixed,
   689                                   nsGkAtoms::_true, eCaseMatters) &&
   690             !content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
   691                                   nsGkAtoms::_true, eCaseMatters)) {
   692             if (count < childIndex && (resizeBefore != Flex || flex > 0)) {
   693                 mChildInfosBefore[mChildInfosBeforeCount].childElem = content;
   694                 mChildInfosBefore[mChildInfosBeforeCount].min     = isHorizontal ? minSize.width : minSize.height;
   695                 mChildInfosBefore[mChildInfosBeforeCount].max     = isHorizontal ? maxSize.width : maxSize.height;
   696                 mChildInfosBefore[mChildInfosBeforeCount].current = isHorizontal ? r.width : r.height;
   697                 mChildInfosBefore[mChildInfosBeforeCount].flex    = flex;
   698                 mChildInfosBefore[mChildInfosBeforeCount].index   = count;
   699                 mChildInfosBefore[mChildInfosBeforeCount].changed = mChildInfosBefore[mChildInfosBeforeCount].current;
   700                 mChildInfosBeforeCount++;
   701             } else if (count > childIndex && (resizeAfter != Flex || flex > 0)) {
   702                 mChildInfosAfter[mChildInfosAfterCount].childElem = content;
   703                 mChildInfosAfter[mChildInfosAfterCount].min     = isHorizontal ? minSize.width : minSize.height;
   704                 mChildInfosAfter[mChildInfosAfterCount].max     = isHorizontal ? maxSize.width : maxSize.height;
   705                 mChildInfosAfter[mChildInfosAfterCount].current = isHorizontal ? r.width : r.height;
   706                 mChildInfosAfter[mChildInfosAfterCount].flex    = flex;
   707                 mChildInfosAfter[mChildInfosAfterCount].index   = count;
   708                 mChildInfosAfter[mChildInfosAfterCount].changed = mChildInfosAfter[mChildInfosAfterCount].current;
   709                 mChildInfosAfterCount++;
   710             }
   711         } 
   712     }
   714     childBox = childBox->GetNextBox();
   715     count++;
   716   }
   718   if (!mParentBox->IsNormalDirection()) {
   719     // The before array is really the after array, and the order needs to be reversed.
   720     // First reverse both arrays.
   721     Reverse(mChildInfosBefore, mChildInfosBeforeCount);
   722     Reverse(mChildInfosAfter, mChildInfosAfterCount);
   724     // Now swap the two arrays.
   725     nscoord newAfterCount = mChildInfosBeforeCount;
   726     mChildInfosBeforeCount = mChildInfosAfterCount;
   727     mChildInfosAfterCount = newAfterCount;
   728     nsSplitterInfo* temp = mChildInfosAfter;
   729     mChildInfosAfter = mChildInfosBefore;
   730     mChildInfosBefore = temp;
   731   }
   733   // if resizebefore is not Farthest, reverse the list because the first child
   734   // in the list is the farthest, and we want the first child to be the closest.
   735   if (resizeBefore != Farthest)
   736      Reverse(mChildInfosBefore, mChildInfosBeforeCount);
   738   // if the resizeafter is the Farthest we must reverse the list because the first child in the list
   739   // is the closest we want the first child to be the Farthest.
   740   if (resizeAfter == Farthest)
   741      Reverse(mChildInfosAfter, mChildInfosAfterCount);
   743   // grow only applys to the children after. If grow is set then no space should be taken out of any children after
   744   // us. To do this we just set the size of that list to be 0.
   745   if (resizeAfter == Grow)
   746      mChildInfosAfterCount = 0;
   748   int32_t c;
   749   nsPoint pt = nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(mouseEvent,
   750                                                                mParentBox);
   751   if (isHorizontal) {
   752      c = pt.x;
   753      mSplitterPos = mOuter->mRect.x;
   754   } else {
   755      c = pt.y;
   756      mSplitterPos = mOuter->mRect.y;
   757   }
   759   mDragStart = c;
   761   //printf("Pressed mDragStart=%d\n",mDragStart);
   763   nsIPresShell::SetCapturingContent(mOuter->GetContent(), CAPTURE_IGNOREALLOWED);
   765   return NS_OK;
   766 }
   768 nsresult
   769 nsSplitterFrameInner::MouseMove(nsIDOMEvent* aMouseEvent)
   770 {  
   771   NS_ENSURE_TRUE(mOuter, NS_OK);
   772   if (!mPressed)
   773     return NS_OK;
   775   if (mDragging)
   776     return NS_OK;
   778   nsCOMPtr<nsIDOMEventListener> kungfuDeathGrip(this);
   779   mOuter->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::state,
   780                             NS_LITERAL_STRING("dragging"), true);
   782   RemoveListener();
   783   mDragging = true;
   785   return NS_OK;
   786 }
   788 void
   789 nsSplitterFrameInner::Reverse(nsSplitterInfo*& aChildInfos, int32_t aCount)
   790 {
   791     nsSplitterInfo* infos = new nsSplitterInfo[aCount];
   793     for (int i=0; i < aCount; i++)
   794        infos[i] = aChildInfos[aCount - 1 - i];
   796     delete[] aChildInfos;
   797     aChildInfos = infos;
   798 }
   800 bool
   801 nsSplitterFrameInner::SupportsCollapseDirection
   802 (
   803   nsSplitterFrameInner::CollapseDirection aDirection
   804 )
   805 {
   806   static nsIContent::AttrValuesArray strings[] =
   807     {&nsGkAtoms::before, &nsGkAtoms::after, &nsGkAtoms::both, nullptr};
   809   switch (mOuter->mContent->FindAttrValueIn(kNameSpaceID_None,
   810                                             nsGkAtoms::collapse,
   811                                             strings, eCaseMatters)) {
   812     case 0:
   813       return (aDirection == Before);
   814     case 1:
   815       return (aDirection == After);
   816     case 2:
   817       return true;
   818   }
   820   return false;
   821 }
   823 void
   824 nsSplitterFrameInner::UpdateState()
   825 {
   826   // State Transitions:
   827   //   Open            -> Dragging
   828   //   Open            -> CollapsedBefore
   829   //   Open            -> CollapsedAfter
   830   //   CollapsedBefore -> Open
   831   //   CollapsedBefore -> Dragging
   832   //   CollapsedAfter  -> Open
   833   //   CollapsedAfter  -> Dragging
   834   //   Dragging        -> Open
   835   //   Dragging        -> CollapsedBefore (auto collapse)
   836   //   Dragging        -> CollapsedAfter (auto collapse)
   838   State newState = GetState(); 
   840   if (newState == mState) {
   841     // No change.
   842     return;
   843   }
   845   if ((SupportsCollapseDirection(Before) || SupportsCollapseDirection(After)) &&
   846       mOuter->GetParent()->IsBoxFrame()) {
   847     // Find the splitter's immediate sibling.
   848     nsIFrame* splitterSibling;
   849     if (newState == CollapsedBefore || mState == CollapsedBefore) {
   850       splitterSibling = mOuter->GetPrevSibling();
   851     } else {
   852       splitterSibling = mOuter->GetNextSibling();
   853     }
   855     if (splitterSibling) {
   856       nsCOMPtr<nsIContent> sibling = splitterSibling->GetContent();
   857       if (sibling) {
   858         if (mState == CollapsedBefore || mState == CollapsedAfter) {
   859           // CollapsedBefore -> Open
   860           // CollapsedBefore -> Dragging
   861           // CollapsedAfter -> Open
   862           // CollapsedAfter -> Dragging
   863           nsContentUtils::AddScriptRunner(
   864             new nsUnsetAttrRunnable(sibling, nsGkAtoms::collapsed));
   865         } else if ((mState == Open || mState == Dragging)
   866                    && (newState == CollapsedBefore ||
   867                        newState == CollapsedAfter)) {
   868           // Open -> CollapsedBefore / CollapsedAfter
   869           // Dragging -> CollapsedBefore / CollapsedAfter
   870           nsContentUtils::AddScriptRunner(
   871             new nsSetAttrRunnable(sibling, nsGkAtoms::collapsed,
   872                                   NS_LITERAL_STRING("true")));
   873         }
   874       }
   875     }
   876   }
   877   mState = newState;
   878 }
   880 void
   881 nsSplitterFrameInner::EnsureOrient()
   882 {
   883   bool isHorizontal = !(mParentBox->GetStateBits() & NS_STATE_IS_HORIZONTAL);
   884   if (isHorizontal)
   885     mOuter->mState |= NS_STATE_IS_HORIZONTAL;
   886   else
   887     mOuter->mState &= ~NS_STATE_IS_HORIZONTAL;
   888 }
   890 void
   891 nsSplitterFrameInner::AdjustChildren(nsPresContext* aPresContext)
   892 {
   893   EnsureOrient();
   894   bool isHorizontal = !mOuter->IsHorizontal();
   896   AdjustChildren(aPresContext, mChildInfosBefore, mChildInfosBeforeCount, isHorizontal);
   897   AdjustChildren(aPresContext, mChildInfosAfter, mChildInfosAfterCount, isHorizontal);
   898 }
   900 static nsIFrame* GetChildBoxForContent(nsIFrame* aParentBox, nsIContent* aContent)
   901 {
   902   nsIFrame* childBox = aParentBox->GetChildBox();
   904   while (nullptr != childBox) {
   905     if (childBox->GetContent() == aContent) {
   906       return childBox;
   907     }
   908     childBox = childBox->GetNextBox();
   909   }
   910   return nullptr;
   911 }
   913 void
   914 nsSplitterFrameInner::AdjustChildren(nsPresContext* aPresContext, nsSplitterInfo* aChildInfos, int32_t aCount, bool aIsHorizontal)
   915 {
   916   ///printf("------- AdjustChildren------\n");
   918   nsBoxLayoutState state(aPresContext);
   920   nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
   922   // first set all the widths.
   923   nsIFrame* child =  mOuter->GetChildBox();
   924   while(child)
   925   {
   926     SetPreferredSize(state, child, onePixel, aIsHorizontal, nullptr);
   927     child = child->GetNextBox();
   928   }
   930   // now set our changed widths.
   931   for (int i=0; i < aCount; i++) 
   932   {
   933     nscoord   pref       = aChildInfos[i].changed;
   934     nsIFrame* childBox     = GetChildBoxForContent(mParentBox, aChildInfos[i].childElem);
   936     if (childBox) {
   937       SetPreferredSize(state, childBox, onePixel, aIsHorizontal, &pref);
   938     }
   939   }
   940 }
   942 void
   943 nsSplitterFrameInner::SetPreferredSize(nsBoxLayoutState& aState, nsIFrame* aChildBox, nscoord aOnePixel, bool aIsHorizontal, nscoord* aSize)
   944 {
   945   nsRect rect(aChildBox->GetRect());
   946   nscoord pref = 0;
   948   if (!aSize)
   949   {
   950     if (aIsHorizontal) 
   951       pref = rect.width;
   952     else
   953       pref = rect.height;
   954   } else {
   955     pref = *aSize;
   956   }
   958   nsMargin margin(0,0,0,0);
   959   aChildBox->GetMargin(margin);
   961   nsCOMPtr<nsIAtom> attribute;
   963   if (aIsHorizontal) {
   964     pref -= (margin.left + margin.right);
   965     attribute = nsGkAtoms::width;
   966   } else {
   967     pref -= (margin.top + margin.bottom);
   968     attribute = nsGkAtoms::height;
   969   }
   971   nsIContent* content = aChildBox->GetContent();
   973   // set its preferred size.
   974   nsAutoString prefValue;
   975   prefValue.AppendInt(pref/aOnePixel);
   976   if (content->AttrValueIs(kNameSpaceID_None, attribute,
   977                            prefValue, eCaseMatters))
   978      return;
   980   nsWeakFrame weakBox(aChildBox);
   981   content->SetAttr(kNameSpaceID_None, attribute, prefValue, true);
   982   ENSURE_TRUE(weakBox.IsAlive());
   983   aState.PresShell()->FrameNeedsReflow(aChildBox, nsIPresShell::eStyleChange,
   984                                        NS_FRAME_IS_DIRTY);
   985 }
   988 void 
   989 nsSplitterFrameInner::AddRemoveSpace(nscoord aDiff,
   990                                     nsSplitterInfo* aChildInfos,
   991                                     int32_t aCount,
   992                                     int32_t& aSpaceLeft)
   993 {
   994   aSpaceLeft = 0;
   996   for (int i=0; i < aCount; i++) {   
   997     nscoord min    = aChildInfos[i].min;
   998     nscoord max    = aChildInfos[i].max;
   999     nscoord& c     = aChildInfos[i].changed;
  1001     // figure our how much space to add or remove
  1002     if (c + aDiff < min) {
  1003       aDiff += (c - min);
  1004       c = min;
  1005     } else if (c + aDiff > max) {
  1006       aDiff -= (max - c);
  1007       c = max;
  1008     } else {
  1009       c += aDiff;
  1010       aDiff = 0;
  1013     // there is not space left? We are done
  1014     if (aDiff == 0)
  1015       break;
  1018   aSpaceLeft = aDiff;
  1021 /**
  1022  * Ok if we want to resize a child we will know the actual size in pixels we want it to be.
  1023  * This is not the preferred size. But they only way we can change a child is my manipulating its
  1024  * preferred size. So give the actual pixel size this return method will return figure out the preferred
  1025  * size and set it.
  1026  */
  1028 void
  1029 nsSplitterFrameInner::ResizeChildTo(nsPresContext* aPresContext,
  1030                                    nscoord& aDiff, 
  1031                                    nsSplitterInfo* aChildrenBeforeInfos, 
  1032                                    nsSplitterInfo* aChildrenAfterInfos, 
  1033                                    int32_t aChildrenBeforeCount, 
  1034                                    int32_t aChildrenAfterCount, 
  1035                                    bool aBounded)
  1037   nscoord spaceLeft;
  1038   AddRemoveSpace(aDiff, aChildrenBeforeInfos,aChildrenBeforeCount,spaceLeft);
  1040   // if there is any space left over remove it from the dif we were originally given
  1041   aDiff -= spaceLeft;
  1042   AddRemoveSpace(-aDiff, aChildrenAfterInfos,aChildrenAfterCount,spaceLeft);
  1044   if (spaceLeft != 0) {
  1045     if (aBounded) {
  1046        aDiff += spaceLeft;
  1047        AddRemoveSpace(spaceLeft, aChildrenBeforeInfos,aChildrenBeforeCount,spaceLeft);
  1048     } else {
  1049       spaceLeft = 0;

mercurial