1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/xul/nsSplitterFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1052 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +// 1.10 +// Eric Vaughan 1.11 +// Netscape Communications 1.12 +// 1.13 +// See documentation in associated header file 1.14 +// 1.15 + 1.16 +#include "nsSplitterFrame.h" 1.17 +#include "nsGkAtoms.h" 1.18 +#include "nsIDOMElement.h" 1.19 +#include "nsIDOMXULElement.h" 1.20 +#include "nsPresContext.h" 1.21 +#include "nsRenderingContext.h" 1.22 +#include "nsIDocument.h" 1.23 +#include "nsNameSpaceManager.h" 1.24 +#include "nsScrollbarButtonFrame.h" 1.25 +#include "nsIDOMEventListener.h" 1.26 +#include "nsIDOMMouseEvent.h" 1.27 +#include "nsIPresShell.h" 1.28 +#include "nsFrameList.h" 1.29 +#include "nsHTMLParts.h" 1.30 +#include "nsStyleContext.h" 1.31 +#include "nsBoxLayoutState.h" 1.32 +#include "nsIServiceManager.h" 1.33 +#include "nsContainerFrame.h" 1.34 +#include "nsAutoPtr.h" 1.35 +#include "nsContentCID.h" 1.36 +#include "nsStyleSet.h" 1.37 +#include "nsLayoutUtils.h" 1.38 +#include "nsDisplayList.h" 1.39 +#include "nsContentUtils.h" 1.40 +#include "mozilla/dom/Element.h" 1.41 +#include "mozilla/MouseEvents.h" 1.42 + 1.43 +using namespace mozilla; 1.44 + 1.45 +class nsSplitterInfo { 1.46 +public: 1.47 + nscoord min; 1.48 + nscoord max; 1.49 + nscoord current; 1.50 + nscoord changed; 1.51 + nsCOMPtr<nsIContent> childElem; 1.52 + int32_t flex; 1.53 + int32_t index; 1.54 +}; 1.55 + 1.56 +class nsSplitterFrameInner : public nsIDOMEventListener 1.57 +{ 1.58 +public: 1.59 + 1.60 + NS_DECL_ISUPPORTS 1.61 + NS_DECL_NSIDOMEVENTLISTENER 1.62 + 1.63 + nsSplitterFrameInner(nsSplitterFrame* aSplitter) 1.64 + { 1.65 + mOuter = aSplitter; 1.66 + mPressed = false; 1.67 + } 1.68 + virtual ~nsSplitterFrameInner(); 1.69 + 1.70 + void Disconnect() { mOuter = nullptr; } 1.71 + 1.72 + nsresult MouseDown(nsIDOMEvent* aMouseEvent); 1.73 + nsresult MouseUp(nsIDOMEvent* aMouseEvent); 1.74 + nsresult MouseMove(nsIDOMEvent* aMouseEvent); 1.75 + 1.76 + void MouseDrag(nsPresContext* aPresContext, WidgetGUIEvent* aEvent); 1.77 + void MouseUp(nsPresContext* aPresContext, WidgetGUIEvent* aEvent); 1.78 + 1.79 + void AdjustChildren(nsPresContext* aPresContext); 1.80 + void AdjustChildren(nsPresContext* aPresContext, nsSplitterInfo* aChildInfos, int32_t aCount, bool aIsHorizontal); 1.81 + 1.82 + void AddRemoveSpace(nscoord aDiff, 1.83 + nsSplitterInfo* aChildInfos, 1.84 + int32_t aCount, 1.85 + int32_t& aSpaceLeft); 1.86 + 1.87 + void ResizeChildTo(nsPresContext* aPresContext, 1.88 + nscoord& aDiff, 1.89 + nsSplitterInfo* aChildrenBeforeInfos, 1.90 + nsSplitterInfo* aChildrenAfterInfos, 1.91 + int32_t aChildrenBeforeCount, 1.92 + int32_t aChildrenAfterCount, 1.93 + bool aBounded); 1.94 + 1.95 + void UpdateState(); 1.96 + 1.97 + void AddListener(nsPresContext* aPresContext); 1.98 + void RemoveListener(); 1.99 + 1.100 + enum ResizeType { Closest, Farthest, Flex, Grow }; 1.101 + enum State { Open, CollapsedBefore, CollapsedAfter, Dragging }; 1.102 + enum CollapseDirection { Before, After }; 1.103 + 1.104 + ResizeType GetResizeBefore(); 1.105 + ResizeType GetResizeAfter(); 1.106 + State GetState(); 1.107 + 1.108 + void Reverse(nsSplitterInfo*& aIndexes, int32_t aCount); 1.109 + bool SupportsCollapseDirection(CollapseDirection aDirection); 1.110 + 1.111 + void EnsureOrient(); 1.112 + void SetPreferredSize(nsBoxLayoutState& aState, nsIFrame* aChildBox, nscoord aOnePixel, bool aIsHorizontal, nscoord* aSize); 1.113 + 1.114 + nsSplitterFrame* mOuter; 1.115 + bool mDidDrag; 1.116 + nscoord mDragStart; 1.117 + nscoord mCurrentPos; 1.118 + nsIFrame* mParentBox; 1.119 + bool mPressed; 1.120 + nsSplitterInfo* mChildInfosBefore; 1.121 + nsSplitterInfo* mChildInfosAfter; 1.122 + int32_t mChildInfosBeforeCount; 1.123 + int32_t mChildInfosAfterCount; 1.124 + State mState; 1.125 + nscoord mSplitterPos; 1.126 + bool mDragging; 1.127 + 1.128 +}; 1.129 + 1.130 +NS_IMPL_ISUPPORTS(nsSplitterFrameInner, nsIDOMEventListener) 1.131 + 1.132 +nsSplitterFrameInner::ResizeType 1.133 +nsSplitterFrameInner::GetResizeBefore() 1.134 +{ 1.135 + static nsIContent::AttrValuesArray strings[] = 1.136 + {&nsGkAtoms::farthest, &nsGkAtoms::flex, nullptr}; 1.137 + switch (mOuter->GetContent()->FindAttrValueIn(kNameSpaceID_None, 1.138 + nsGkAtoms::resizebefore, 1.139 + strings, eCaseMatters)) { 1.140 + case 0: return Farthest; 1.141 + case 1: return Flex; 1.142 + } 1.143 + return Closest; 1.144 +} 1.145 + 1.146 +nsSplitterFrameInner::~nsSplitterFrameInner() 1.147 +{ 1.148 + delete[] mChildInfosBefore; 1.149 + delete[] mChildInfosAfter; 1.150 +} 1.151 + 1.152 +nsSplitterFrameInner::ResizeType 1.153 +nsSplitterFrameInner::GetResizeAfter() 1.154 +{ 1.155 + static nsIContent::AttrValuesArray strings[] = 1.156 + {&nsGkAtoms::farthest, &nsGkAtoms::flex, &nsGkAtoms::grow, nullptr}; 1.157 + switch (mOuter->GetContent()->FindAttrValueIn(kNameSpaceID_None, 1.158 + nsGkAtoms::resizeafter, 1.159 + strings, eCaseMatters)) { 1.160 + case 0: return Farthest; 1.161 + case 1: return Flex; 1.162 + case 2: return Grow; 1.163 + } 1.164 + return Closest; 1.165 +} 1.166 + 1.167 +nsSplitterFrameInner::State 1.168 +nsSplitterFrameInner::GetState() 1.169 +{ 1.170 + static nsIContent::AttrValuesArray strings[] = 1.171 + {&nsGkAtoms::dragging, &nsGkAtoms::collapsed, nullptr}; 1.172 + static nsIContent::AttrValuesArray strings_substate[] = 1.173 + {&nsGkAtoms::before, &nsGkAtoms::after, nullptr}; 1.174 + switch (mOuter->GetContent()->FindAttrValueIn(kNameSpaceID_None, 1.175 + nsGkAtoms::state, 1.176 + strings, eCaseMatters)) { 1.177 + case 0: return Dragging; 1.178 + case 1: 1.179 + switch (mOuter->GetContent()->FindAttrValueIn(kNameSpaceID_None, 1.180 + nsGkAtoms::substate, 1.181 + strings_substate, 1.182 + eCaseMatters)) { 1.183 + case 0: return CollapsedBefore; 1.184 + case 1: return CollapsedAfter; 1.185 + default: 1.186 + if (SupportsCollapseDirection(After)) 1.187 + return CollapsedAfter; 1.188 + return CollapsedBefore; 1.189 + } 1.190 + } 1.191 + return Open; 1.192 +} 1.193 + 1.194 +// 1.195 +// NS_NewSplitterFrame 1.196 +// 1.197 +// Creates a new Toolbar frame and returns it 1.198 +// 1.199 +nsIFrame* 1.200 +NS_NewSplitterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) 1.201 +{ 1.202 + return new (aPresShell) nsSplitterFrame(aPresShell, aContext); 1.203 +} 1.204 + 1.205 +NS_IMPL_FRAMEARENA_HELPERS(nsSplitterFrame) 1.206 + 1.207 +nsSplitterFrame::nsSplitterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) 1.208 +: nsBoxFrame(aPresShell, aContext), 1.209 + mInner(0) 1.210 +{ 1.211 +} 1.212 + 1.213 +void 1.214 +nsSplitterFrame::DestroyFrom(nsIFrame* aDestructRoot) 1.215 +{ 1.216 + if (mInner) { 1.217 + mInner->RemoveListener(); 1.218 + mInner->Disconnect(); 1.219 + mInner->Release(); 1.220 + mInner = nullptr; 1.221 + } 1.222 + nsBoxFrame::DestroyFrom(aDestructRoot); 1.223 +} 1.224 + 1.225 + 1.226 +nsresult 1.227 +nsSplitterFrame::GetCursor(const nsPoint& aPoint, 1.228 + nsIFrame::Cursor& aCursor) 1.229 +{ 1.230 + return nsBoxFrame::GetCursor(aPoint, aCursor); 1.231 + 1.232 + /* 1.233 + if (IsHorizontal()) 1.234 + aCursor = NS_STYLE_CURSOR_N_RESIZE; 1.235 + else 1.236 + aCursor = NS_STYLE_CURSOR_W_RESIZE; 1.237 + 1.238 + return NS_OK; 1.239 + */ 1.240 +} 1.241 + 1.242 +nsresult 1.243 +nsSplitterFrame::AttributeChanged(int32_t aNameSpaceID, 1.244 + nsIAtom* aAttribute, 1.245 + int32_t aModType) 1.246 +{ 1.247 + nsresult rv = nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, 1.248 + aModType); 1.249 + // if the alignment changed. Let the grippy know 1.250 + if (aAttribute == nsGkAtoms::align) { 1.251 + // tell the slider its attribute changed so it can 1.252 + // update itself 1.253 + nsIFrame* grippy = nullptr; 1.254 + nsScrollbarButtonFrame::GetChildWithTag(PresContext(), nsGkAtoms::grippy, this, grippy); 1.255 + if (grippy) 1.256 + grippy->AttributeChanged(aNameSpaceID, aAttribute, aModType); 1.257 + } else if (aAttribute == nsGkAtoms::state) { 1.258 + mInner->UpdateState(); 1.259 + } 1.260 + 1.261 + return rv; 1.262 +} 1.263 + 1.264 +/** 1.265 + * Initialize us. If we are in a box get our alignment so we know what direction we are 1.266 + */ 1.267 +void 1.268 +nsSplitterFrame::Init(nsIContent* aContent, 1.269 + nsIFrame* aParent, 1.270 + nsIFrame* aPrevInFlow) 1.271 +{ 1.272 + MOZ_ASSERT(!mInner); 1.273 + mInner = new nsSplitterFrameInner(this); 1.274 + 1.275 + mInner->AddRef(); 1.276 + mInner->mChildInfosAfter = nullptr; 1.277 + mInner->mChildInfosBefore = nullptr; 1.278 + mInner->mState = nsSplitterFrameInner::Open; 1.279 + mInner->mDragging = false; 1.280 + 1.281 + // determine orientation of parent, and if vertical, set orient to vertical 1.282 + // on splitter content, then re-resolve style 1.283 + // XXXbz this is pretty messed up, since this can change whether we should 1.284 + // have a frame at all. This really needs a better solution. 1.285 + if (aParent && aParent->IsBoxFrame()) { 1.286 + if (!aParent->IsHorizontal()) { 1.287 + if (!nsContentUtils::HasNonEmptyAttr(aContent, kNameSpaceID_None, 1.288 + nsGkAtoms::orient)) { 1.289 + aContent->SetAttr(kNameSpaceID_None, nsGkAtoms::orient, 1.290 + NS_LITERAL_STRING("vertical"), false); 1.291 + nsStyleContext* parentStyleContext = StyleContext()->GetParent(); 1.292 + nsRefPtr<nsStyleContext> newContext = PresContext()->StyleSet()-> 1.293 + ResolveStyleFor(aContent->AsElement(), parentStyleContext); 1.294 + SetStyleContextWithoutNotification(newContext); 1.295 + } 1.296 + } 1.297 + } 1.298 + 1.299 + nsBoxFrame::Init(aContent, aParent, aPrevInFlow); 1.300 + 1.301 + mInner->mState = nsSplitterFrameInner::Open; 1.302 + mInner->AddListener(PresContext()); 1.303 + mInner->mParentBox = nullptr; 1.304 +} 1.305 + 1.306 +NS_IMETHODIMP 1.307 +nsSplitterFrame::DoLayout(nsBoxLayoutState& aState) 1.308 +{ 1.309 + if (GetStateBits() & NS_FRAME_FIRST_REFLOW) 1.310 + { 1.311 + mInner->mParentBox = GetParentBox(); 1.312 + mInner->UpdateState(); 1.313 + } 1.314 + 1.315 + return nsBoxFrame::DoLayout(aState); 1.316 +} 1.317 + 1.318 + 1.319 +void 1.320 +nsSplitterFrame::GetInitialOrientation(bool& aIsHorizontal) 1.321 +{ 1.322 + nsIFrame* box = GetParentBox(); 1.323 + if (box) { 1.324 + aIsHorizontal = !box->IsHorizontal(); 1.325 + } 1.326 + else 1.327 + nsBoxFrame::GetInitialOrientation(aIsHorizontal); 1.328 +} 1.329 + 1.330 +NS_IMETHODIMP 1.331 +nsSplitterFrame::HandlePress(nsPresContext* aPresContext, 1.332 + WidgetGUIEvent* aEvent, 1.333 + nsEventStatus* aEventStatus) 1.334 +{ 1.335 + return NS_OK; 1.336 +} 1.337 + 1.338 +NS_IMETHODIMP 1.339 +nsSplitterFrame::HandleMultiplePress(nsPresContext* aPresContext, 1.340 + WidgetGUIEvent* aEvent, 1.341 + nsEventStatus* aEventStatus, 1.342 + bool aControlHeld) 1.343 +{ 1.344 + return NS_OK; 1.345 +} 1.346 + 1.347 +NS_IMETHODIMP 1.348 +nsSplitterFrame::HandleDrag(nsPresContext* aPresContext, 1.349 + WidgetGUIEvent* aEvent, 1.350 + nsEventStatus* aEventStatus) 1.351 +{ 1.352 + return NS_OK; 1.353 +} 1.354 + 1.355 +NS_IMETHODIMP 1.356 +nsSplitterFrame::HandleRelease(nsPresContext* aPresContext, 1.357 + WidgetGUIEvent* aEvent, 1.358 + nsEventStatus* aEventStatus) 1.359 +{ 1.360 + return NS_OK; 1.361 +} 1.362 + 1.363 +void 1.364 +nsSplitterFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.365 + const nsRect& aDirtyRect, 1.366 + const nsDisplayListSet& aLists) 1.367 +{ 1.368 + nsBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists); 1.369 + 1.370 + // if the mouse is captured always return us as the frame. 1.371 + if (mInner->mDragging) 1.372 + { 1.373 + // XXX It's probably better not to check visibility here, right? 1.374 + aLists.Outlines()->AppendNewToTop(new (aBuilder) 1.375 + nsDisplayEventReceiver(aBuilder, this)); 1.376 + return; 1.377 + } 1.378 +} 1.379 + 1.380 +nsresult 1.381 +nsSplitterFrame::HandleEvent(nsPresContext* aPresContext, 1.382 + WidgetGUIEvent* aEvent, 1.383 + nsEventStatus* aEventStatus) 1.384 +{ 1.385 + NS_ENSURE_ARG_POINTER(aEventStatus); 1.386 + if (nsEventStatus_eConsumeNoDefault == *aEventStatus) { 1.387 + return NS_OK; 1.388 + } 1.389 + 1.390 + nsWeakFrame weakFrame(this); 1.391 + nsRefPtr<nsSplitterFrameInner> kungFuDeathGrip(mInner); 1.392 + switch (aEvent->message) { 1.393 + case NS_MOUSE_MOVE: 1.394 + mInner->MouseDrag(aPresContext, aEvent); 1.395 + break; 1.396 + 1.397 + case NS_MOUSE_BUTTON_UP: 1.398 + if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) { 1.399 + mInner->MouseUp(aPresContext, aEvent); 1.400 + } 1.401 + break; 1.402 + } 1.403 + 1.404 + NS_ENSURE_STATE(weakFrame.IsAlive()); 1.405 + return nsBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus); 1.406 +} 1.407 + 1.408 +void 1.409 +nsSplitterFrameInner::MouseUp(nsPresContext* aPresContext, 1.410 + WidgetGUIEvent* aEvent) 1.411 +{ 1.412 + if (mDragging && mOuter) { 1.413 + AdjustChildren(aPresContext); 1.414 + AddListener(aPresContext); 1.415 + nsIPresShell::SetCapturingContent(nullptr, 0); // XXXndeakin is this needed? 1.416 + mDragging = false; 1.417 + State newState = GetState(); 1.418 + // if the state is dragging then make it Open. 1.419 + if (newState == Dragging) 1.420 + mOuter->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::state, EmptyString(), true); 1.421 + 1.422 + mPressed = false; 1.423 + 1.424 + // if we dragged then fire a command event. 1.425 + if (mDidDrag) { 1.426 + nsCOMPtr<nsIDOMXULElement> element = do_QueryInterface(mOuter->GetContent()); 1.427 + element->DoCommand(); 1.428 + } 1.429 + 1.430 + //printf("MouseUp\n"); 1.431 + } 1.432 + 1.433 + delete[] mChildInfosBefore; 1.434 + delete[] mChildInfosAfter; 1.435 + mChildInfosBefore = nullptr; 1.436 + mChildInfosAfter = nullptr; 1.437 + mChildInfosBeforeCount = 0; 1.438 + mChildInfosAfterCount = 0; 1.439 +} 1.440 + 1.441 +void 1.442 +nsSplitterFrameInner::MouseDrag(nsPresContext* aPresContext, 1.443 + WidgetGUIEvent* aEvent) 1.444 +{ 1.445 + if (mDragging && mOuter) { 1.446 + 1.447 + //printf("Dragging\n"); 1.448 + 1.449 + bool isHorizontal = !mOuter->IsHorizontal(); 1.450 + // convert coord to pixels 1.451 + nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, 1.452 + mParentBox); 1.453 + nscoord pos = isHorizontal ? pt.x : pt.y; 1.454 + 1.455 + // mDragStart is in frame coordinates 1.456 + nscoord start = mDragStart; 1.457 + 1.458 + // take our current position and subtract the start location 1.459 + pos -= start; 1.460 + 1.461 + //printf("Diff=%d\n", pos); 1.462 + 1.463 + ResizeType resizeAfter = GetResizeAfter(); 1.464 + 1.465 + bool bounded; 1.466 + 1.467 + if (resizeAfter == nsSplitterFrameInner::Grow) 1.468 + bounded = false; 1.469 + else 1.470 + bounded = true; 1.471 + 1.472 + int i; 1.473 + for (i=0; i < mChildInfosBeforeCount; i++) 1.474 + mChildInfosBefore[i].changed = mChildInfosBefore[i].current; 1.475 + 1.476 + for (i=0; i < mChildInfosAfterCount; i++) 1.477 + mChildInfosAfter[i].changed = mChildInfosAfter[i].current; 1.478 + 1.479 + nscoord oldPos = pos; 1.480 + 1.481 + ResizeChildTo(aPresContext, pos, mChildInfosBefore, mChildInfosAfter, mChildInfosBeforeCount, mChildInfosAfterCount, bounded); 1.482 + 1.483 + State currentState = GetState(); 1.484 + bool supportsBefore = SupportsCollapseDirection(Before); 1.485 + bool supportsAfter = SupportsCollapseDirection(After); 1.486 + 1.487 + const bool isRTL = mOuter->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; 1.488 + bool pastEnd = oldPos > 0 && oldPos > pos; 1.489 + bool pastBegin = oldPos < 0 && oldPos < pos; 1.490 + if (isRTL) { 1.491 + // Swap the boundary checks in RTL mode 1.492 + bool tmp = pastEnd; 1.493 + pastEnd = pastBegin; 1.494 + pastBegin = tmp; 1.495 + } 1.496 + const bool isCollapsedBefore = pastBegin && supportsBefore; 1.497 + const bool isCollapsedAfter = pastEnd && supportsAfter; 1.498 + 1.499 + // if we are in a collapsed position 1.500 + if (isCollapsedBefore || isCollapsedAfter) 1.501 + { 1.502 + // and we are not collapsed then collapse 1.503 + if (currentState == Dragging) { 1.504 + if (pastEnd) 1.505 + { 1.506 + //printf("Collapse right\n"); 1.507 + if (supportsAfter) 1.508 + { 1.509 + nsCOMPtr<nsIContent> outer = mOuter->mContent; 1.510 + outer->SetAttr(kNameSpaceID_None, nsGkAtoms::substate, 1.511 + NS_LITERAL_STRING("after"), 1.512 + true); 1.513 + outer->SetAttr(kNameSpaceID_None, nsGkAtoms::state, 1.514 + NS_LITERAL_STRING("collapsed"), 1.515 + true); 1.516 + } 1.517 + 1.518 + } else if (pastBegin) 1.519 + { 1.520 + //printf("Collapse left\n"); 1.521 + if (supportsBefore) 1.522 + { 1.523 + nsCOMPtr<nsIContent> outer = mOuter->mContent; 1.524 + outer->SetAttr(kNameSpaceID_None, nsGkAtoms::substate, 1.525 + NS_LITERAL_STRING("before"), 1.526 + true); 1.527 + outer->SetAttr(kNameSpaceID_None, nsGkAtoms::state, 1.528 + NS_LITERAL_STRING("collapsed"), 1.529 + true); 1.530 + } 1.531 + } 1.532 + } 1.533 + } else { 1.534 + // if we are not in a collapsed position and we are not dragging make sure 1.535 + // we are dragging. 1.536 + if (currentState != Dragging) 1.537 + mOuter->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::state, NS_LITERAL_STRING("dragging"), true); 1.538 + AdjustChildren(aPresContext); 1.539 + } 1.540 + 1.541 + mDidDrag = true; 1.542 + } 1.543 +} 1.544 + 1.545 +void 1.546 +nsSplitterFrameInner::AddListener(nsPresContext* aPresContext) 1.547 +{ 1.548 + mOuter->GetContent()-> 1.549 + AddEventListener(NS_LITERAL_STRING("mouseup"), this, false, false); 1.550 + mOuter->GetContent()-> 1.551 + AddEventListener(NS_LITERAL_STRING("mousedown"), this, false, false); 1.552 + mOuter->GetContent()-> 1.553 + AddEventListener(NS_LITERAL_STRING("mousemove"), this, false, false); 1.554 + mOuter->GetContent()-> 1.555 + AddEventListener(NS_LITERAL_STRING("mouseout"), this, false, false); 1.556 +} 1.557 + 1.558 +void 1.559 +nsSplitterFrameInner::RemoveListener() 1.560 +{ 1.561 + ENSURE_TRUE(mOuter); 1.562 + mOuter->GetContent()-> 1.563 + RemoveEventListener(NS_LITERAL_STRING("mouseup"), this, false); 1.564 + mOuter->GetContent()-> 1.565 + RemoveEventListener(NS_LITERAL_STRING("mousedown"), this, false); 1.566 + mOuter->GetContent()-> 1.567 + RemoveEventListener(NS_LITERAL_STRING("mousemove"), this, false); 1.568 + mOuter->GetContent()-> 1.569 + RemoveEventListener(NS_LITERAL_STRING("mouseout"), this, false); 1.570 +} 1.571 + 1.572 +nsresult 1.573 +nsSplitterFrameInner::HandleEvent(nsIDOMEvent* aEvent) 1.574 +{ 1.575 + nsAutoString eventType; 1.576 + aEvent->GetType(eventType); 1.577 + if (eventType.EqualsLiteral("mouseup")) 1.578 + return MouseUp(aEvent); 1.579 + if (eventType.EqualsLiteral("mousedown")) 1.580 + return MouseDown(aEvent); 1.581 + if (eventType.EqualsLiteral("mousemove") || 1.582 + eventType.EqualsLiteral("mouseout")) 1.583 + return MouseMove(aEvent); 1.584 + 1.585 + NS_ABORT(); 1.586 + return NS_OK; 1.587 +} 1.588 + 1.589 +nsresult 1.590 +nsSplitterFrameInner::MouseUp(nsIDOMEvent* aMouseEvent) 1.591 +{ 1.592 + NS_ENSURE_TRUE(mOuter, NS_OK); 1.593 + mPressed = false; 1.594 + 1.595 + nsIPresShell::SetCapturingContent(nullptr, 0); 1.596 + 1.597 + return NS_OK; 1.598 +} 1.599 + 1.600 +nsresult 1.601 +nsSplitterFrameInner::MouseDown(nsIDOMEvent* aMouseEvent) 1.602 +{ 1.603 + NS_ENSURE_TRUE(mOuter, NS_OK); 1.604 + nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(aMouseEvent)); 1.605 + if (!mouseEvent) 1.606 + return NS_OK; 1.607 + 1.608 + int16_t button = 0; 1.609 + mouseEvent->GetButton(&button); 1.610 + 1.611 + // only if left button 1.612 + if (button != 0) 1.613 + return NS_OK; 1.614 + 1.615 + if (mOuter->GetContent()-> 1.616 + AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled, 1.617 + nsGkAtoms::_true, eCaseMatters)) 1.618 + return NS_OK; 1.619 + 1.620 + mParentBox = mOuter->GetParentBox(); 1.621 + if (!mParentBox) 1.622 + return NS_OK; 1.623 + 1.624 + // get our index 1.625 + nsPresContext* outerPresContext = mOuter->PresContext(); 1.626 + const nsFrameList& siblingList(mParentBox->PrincipalChildList()); 1.627 + int32_t childIndex = siblingList.IndexOf(mOuter); 1.628 + // if it's 0 (or not found) then stop right here. 1.629 + // It might be not found if we're not in the parent's primary frame list. 1.630 + if (childIndex <= 0) 1.631 + return NS_OK; 1.632 + 1.633 + int32_t childCount = siblingList.GetLength(); 1.634 + // if it's the last index then we need to allow for resizeafter="grow" 1.635 + if (childIndex == childCount - 1 && GetResizeAfter() != Grow) 1.636 + return NS_OK; 1.637 + 1.638 + nsRefPtr<nsRenderingContext> rc = 1.639 + outerPresContext->PresShell()->CreateReferenceRenderingContext(); 1.640 + nsBoxLayoutState state(outerPresContext, rc); 1.641 + mCurrentPos = 0; 1.642 + mPressed = true; 1.643 + 1.644 + mDidDrag = false; 1.645 + 1.646 + EnsureOrient(); 1.647 + bool isHorizontal = !mOuter->IsHorizontal(); 1.648 + 1.649 + ResizeType resizeBefore = GetResizeBefore(); 1.650 + ResizeType resizeAfter = GetResizeAfter(); 1.651 + 1.652 + delete[] mChildInfosBefore; 1.653 + delete[] mChildInfosAfter; 1.654 + mChildInfosBefore = new nsSplitterInfo[childCount]; 1.655 + mChildInfosAfter = new nsSplitterInfo[childCount]; 1.656 + 1.657 + // create info 2 lists. One of the children before us and one after. 1.658 + int32_t count = 0; 1.659 + mChildInfosBeforeCount = 0; 1.660 + mChildInfosAfterCount = 0; 1.661 + 1.662 + nsIFrame* childBox = mParentBox->GetChildBox(); 1.663 + 1.664 + while (nullptr != childBox) 1.665 + { 1.666 + nsIContent* content = childBox->GetContent(); 1.667 + nsIDocument* doc = content->OwnerDoc(); 1.668 + int32_t dummy; 1.669 + nsIAtom* atom = doc->BindingManager()->ResolveTag(content, &dummy); 1.670 + 1.671 + // skip over any splitters 1.672 + if (atom != nsGkAtoms::splitter) { 1.673 + nsSize prefSize = childBox->GetPrefSize(state); 1.674 + nsSize minSize = childBox->GetMinSize(state); 1.675 + nsSize maxSize = nsBox::BoundsCheckMinMax(minSize, childBox->GetMaxSize(state)); 1.676 + prefSize = nsBox::BoundsCheck(minSize, prefSize, maxSize); 1.677 + 1.678 + mOuter->AddMargin(childBox, minSize); 1.679 + mOuter->AddMargin(childBox, prefSize); 1.680 + mOuter->AddMargin(childBox, maxSize); 1.681 + 1.682 + nscoord flex = childBox->GetFlex(state); 1.683 + 1.684 + nsMargin margin(0,0,0,0); 1.685 + childBox->GetMargin(margin); 1.686 + nsRect r(childBox->GetRect()); 1.687 + r.Inflate(margin); 1.688 + 1.689 + // We need to check for hidden attribute too, since treecols with 1.690 + // the hidden="true" attribute are not really hidden, just collapsed 1.691 + if (!content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::fixed, 1.692 + nsGkAtoms::_true, eCaseMatters) && 1.693 + !content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden, 1.694 + nsGkAtoms::_true, eCaseMatters)) { 1.695 + if (count < childIndex && (resizeBefore != Flex || flex > 0)) { 1.696 + mChildInfosBefore[mChildInfosBeforeCount].childElem = content; 1.697 + mChildInfosBefore[mChildInfosBeforeCount].min = isHorizontal ? minSize.width : minSize.height; 1.698 + mChildInfosBefore[mChildInfosBeforeCount].max = isHorizontal ? maxSize.width : maxSize.height; 1.699 + mChildInfosBefore[mChildInfosBeforeCount].current = isHorizontal ? r.width : r.height; 1.700 + mChildInfosBefore[mChildInfosBeforeCount].flex = flex; 1.701 + mChildInfosBefore[mChildInfosBeforeCount].index = count; 1.702 + mChildInfosBefore[mChildInfosBeforeCount].changed = mChildInfosBefore[mChildInfosBeforeCount].current; 1.703 + mChildInfosBeforeCount++; 1.704 + } else if (count > childIndex && (resizeAfter != Flex || flex > 0)) { 1.705 + mChildInfosAfter[mChildInfosAfterCount].childElem = content; 1.706 + mChildInfosAfter[mChildInfosAfterCount].min = isHorizontal ? minSize.width : minSize.height; 1.707 + mChildInfosAfter[mChildInfosAfterCount].max = isHorizontal ? maxSize.width : maxSize.height; 1.708 + mChildInfosAfter[mChildInfosAfterCount].current = isHorizontal ? r.width : r.height; 1.709 + mChildInfosAfter[mChildInfosAfterCount].flex = flex; 1.710 + mChildInfosAfter[mChildInfosAfterCount].index = count; 1.711 + mChildInfosAfter[mChildInfosAfterCount].changed = mChildInfosAfter[mChildInfosAfterCount].current; 1.712 + mChildInfosAfterCount++; 1.713 + } 1.714 + } 1.715 + } 1.716 + 1.717 + childBox = childBox->GetNextBox(); 1.718 + count++; 1.719 + } 1.720 + 1.721 + if (!mParentBox->IsNormalDirection()) { 1.722 + // The before array is really the after array, and the order needs to be reversed. 1.723 + // First reverse both arrays. 1.724 + Reverse(mChildInfosBefore, mChildInfosBeforeCount); 1.725 + Reverse(mChildInfosAfter, mChildInfosAfterCount); 1.726 + 1.727 + // Now swap the two arrays. 1.728 + nscoord newAfterCount = mChildInfosBeforeCount; 1.729 + mChildInfosBeforeCount = mChildInfosAfterCount; 1.730 + mChildInfosAfterCount = newAfterCount; 1.731 + nsSplitterInfo* temp = mChildInfosAfter; 1.732 + mChildInfosAfter = mChildInfosBefore; 1.733 + mChildInfosBefore = temp; 1.734 + } 1.735 + 1.736 + // if resizebefore is not Farthest, reverse the list because the first child 1.737 + // in the list is the farthest, and we want the first child to be the closest. 1.738 + if (resizeBefore != Farthest) 1.739 + Reverse(mChildInfosBefore, mChildInfosBeforeCount); 1.740 + 1.741 + // if the resizeafter is the Farthest we must reverse the list because the first child in the list 1.742 + // is the closest we want the first child to be the Farthest. 1.743 + if (resizeAfter == Farthest) 1.744 + Reverse(mChildInfosAfter, mChildInfosAfterCount); 1.745 + 1.746 + // grow only applys to the children after. If grow is set then no space should be taken out of any children after 1.747 + // us. To do this we just set the size of that list to be 0. 1.748 + if (resizeAfter == Grow) 1.749 + mChildInfosAfterCount = 0; 1.750 + 1.751 + int32_t c; 1.752 + nsPoint pt = nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(mouseEvent, 1.753 + mParentBox); 1.754 + if (isHorizontal) { 1.755 + c = pt.x; 1.756 + mSplitterPos = mOuter->mRect.x; 1.757 + } else { 1.758 + c = pt.y; 1.759 + mSplitterPos = mOuter->mRect.y; 1.760 + } 1.761 + 1.762 + mDragStart = c; 1.763 + 1.764 + //printf("Pressed mDragStart=%d\n",mDragStart); 1.765 + 1.766 + nsIPresShell::SetCapturingContent(mOuter->GetContent(), CAPTURE_IGNOREALLOWED); 1.767 + 1.768 + return NS_OK; 1.769 +} 1.770 + 1.771 +nsresult 1.772 +nsSplitterFrameInner::MouseMove(nsIDOMEvent* aMouseEvent) 1.773 +{ 1.774 + NS_ENSURE_TRUE(mOuter, NS_OK); 1.775 + if (!mPressed) 1.776 + return NS_OK; 1.777 + 1.778 + if (mDragging) 1.779 + return NS_OK; 1.780 + 1.781 + nsCOMPtr<nsIDOMEventListener> kungfuDeathGrip(this); 1.782 + mOuter->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::state, 1.783 + NS_LITERAL_STRING("dragging"), true); 1.784 + 1.785 + RemoveListener(); 1.786 + mDragging = true; 1.787 + 1.788 + return NS_OK; 1.789 +} 1.790 + 1.791 +void 1.792 +nsSplitterFrameInner::Reverse(nsSplitterInfo*& aChildInfos, int32_t aCount) 1.793 +{ 1.794 + nsSplitterInfo* infos = new nsSplitterInfo[aCount]; 1.795 + 1.796 + for (int i=0; i < aCount; i++) 1.797 + infos[i] = aChildInfos[aCount - 1 - i]; 1.798 + 1.799 + delete[] aChildInfos; 1.800 + aChildInfos = infos; 1.801 +} 1.802 + 1.803 +bool 1.804 +nsSplitterFrameInner::SupportsCollapseDirection 1.805 +( 1.806 + nsSplitterFrameInner::CollapseDirection aDirection 1.807 +) 1.808 +{ 1.809 + static nsIContent::AttrValuesArray strings[] = 1.810 + {&nsGkAtoms::before, &nsGkAtoms::after, &nsGkAtoms::both, nullptr}; 1.811 + 1.812 + switch (mOuter->mContent->FindAttrValueIn(kNameSpaceID_None, 1.813 + nsGkAtoms::collapse, 1.814 + strings, eCaseMatters)) { 1.815 + case 0: 1.816 + return (aDirection == Before); 1.817 + case 1: 1.818 + return (aDirection == After); 1.819 + case 2: 1.820 + return true; 1.821 + } 1.822 + 1.823 + return false; 1.824 +} 1.825 + 1.826 +void 1.827 +nsSplitterFrameInner::UpdateState() 1.828 +{ 1.829 + // State Transitions: 1.830 + // Open -> Dragging 1.831 + // Open -> CollapsedBefore 1.832 + // Open -> CollapsedAfter 1.833 + // CollapsedBefore -> Open 1.834 + // CollapsedBefore -> Dragging 1.835 + // CollapsedAfter -> Open 1.836 + // CollapsedAfter -> Dragging 1.837 + // Dragging -> Open 1.838 + // Dragging -> CollapsedBefore (auto collapse) 1.839 + // Dragging -> CollapsedAfter (auto collapse) 1.840 + 1.841 + State newState = GetState(); 1.842 + 1.843 + if (newState == mState) { 1.844 + // No change. 1.845 + return; 1.846 + } 1.847 + 1.848 + if ((SupportsCollapseDirection(Before) || SupportsCollapseDirection(After)) && 1.849 + mOuter->GetParent()->IsBoxFrame()) { 1.850 + // Find the splitter's immediate sibling. 1.851 + nsIFrame* splitterSibling; 1.852 + if (newState == CollapsedBefore || mState == CollapsedBefore) { 1.853 + splitterSibling = mOuter->GetPrevSibling(); 1.854 + } else { 1.855 + splitterSibling = mOuter->GetNextSibling(); 1.856 + } 1.857 + 1.858 + if (splitterSibling) { 1.859 + nsCOMPtr<nsIContent> sibling = splitterSibling->GetContent(); 1.860 + if (sibling) { 1.861 + if (mState == CollapsedBefore || mState == CollapsedAfter) { 1.862 + // CollapsedBefore -> Open 1.863 + // CollapsedBefore -> Dragging 1.864 + // CollapsedAfter -> Open 1.865 + // CollapsedAfter -> Dragging 1.866 + nsContentUtils::AddScriptRunner( 1.867 + new nsUnsetAttrRunnable(sibling, nsGkAtoms::collapsed)); 1.868 + } else if ((mState == Open || mState == Dragging) 1.869 + && (newState == CollapsedBefore || 1.870 + newState == CollapsedAfter)) { 1.871 + // Open -> CollapsedBefore / CollapsedAfter 1.872 + // Dragging -> CollapsedBefore / CollapsedAfter 1.873 + nsContentUtils::AddScriptRunner( 1.874 + new nsSetAttrRunnable(sibling, nsGkAtoms::collapsed, 1.875 + NS_LITERAL_STRING("true"))); 1.876 + } 1.877 + } 1.878 + } 1.879 + } 1.880 + mState = newState; 1.881 +} 1.882 + 1.883 +void 1.884 +nsSplitterFrameInner::EnsureOrient() 1.885 +{ 1.886 + bool isHorizontal = !(mParentBox->GetStateBits() & NS_STATE_IS_HORIZONTAL); 1.887 + if (isHorizontal) 1.888 + mOuter->mState |= NS_STATE_IS_HORIZONTAL; 1.889 + else 1.890 + mOuter->mState &= ~NS_STATE_IS_HORIZONTAL; 1.891 +} 1.892 + 1.893 +void 1.894 +nsSplitterFrameInner::AdjustChildren(nsPresContext* aPresContext) 1.895 +{ 1.896 + EnsureOrient(); 1.897 + bool isHorizontal = !mOuter->IsHorizontal(); 1.898 + 1.899 + AdjustChildren(aPresContext, mChildInfosBefore, mChildInfosBeforeCount, isHorizontal); 1.900 + AdjustChildren(aPresContext, mChildInfosAfter, mChildInfosAfterCount, isHorizontal); 1.901 +} 1.902 + 1.903 +static nsIFrame* GetChildBoxForContent(nsIFrame* aParentBox, nsIContent* aContent) 1.904 +{ 1.905 + nsIFrame* childBox = aParentBox->GetChildBox(); 1.906 + 1.907 + while (nullptr != childBox) { 1.908 + if (childBox->GetContent() == aContent) { 1.909 + return childBox; 1.910 + } 1.911 + childBox = childBox->GetNextBox(); 1.912 + } 1.913 + return nullptr; 1.914 +} 1.915 + 1.916 +void 1.917 +nsSplitterFrameInner::AdjustChildren(nsPresContext* aPresContext, nsSplitterInfo* aChildInfos, int32_t aCount, bool aIsHorizontal) 1.918 +{ 1.919 + ///printf("------- AdjustChildren------\n"); 1.920 + 1.921 + nsBoxLayoutState state(aPresContext); 1.922 + 1.923 + nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); 1.924 + 1.925 + // first set all the widths. 1.926 + nsIFrame* child = mOuter->GetChildBox(); 1.927 + while(child) 1.928 + { 1.929 + SetPreferredSize(state, child, onePixel, aIsHorizontal, nullptr); 1.930 + child = child->GetNextBox(); 1.931 + } 1.932 + 1.933 + // now set our changed widths. 1.934 + for (int i=0; i < aCount; i++) 1.935 + { 1.936 + nscoord pref = aChildInfos[i].changed; 1.937 + nsIFrame* childBox = GetChildBoxForContent(mParentBox, aChildInfos[i].childElem); 1.938 + 1.939 + if (childBox) { 1.940 + SetPreferredSize(state, childBox, onePixel, aIsHorizontal, &pref); 1.941 + } 1.942 + } 1.943 +} 1.944 + 1.945 +void 1.946 +nsSplitterFrameInner::SetPreferredSize(nsBoxLayoutState& aState, nsIFrame* aChildBox, nscoord aOnePixel, bool aIsHorizontal, nscoord* aSize) 1.947 +{ 1.948 + nsRect rect(aChildBox->GetRect()); 1.949 + nscoord pref = 0; 1.950 + 1.951 + if (!aSize) 1.952 + { 1.953 + if (aIsHorizontal) 1.954 + pref = rect.width; 1.955 + else 1.956 + pref = rect.height; 1.957 + } else { 1.958 + pref = *aSize; 1.959 + } 1.960 + 1.961 + nsMargin margin(0,0,0,0); 1.962 + aChildBox->GetMargin(margin); 1.963 + 1.964 + nsCOMPtr<nsIAtom> attribute; 1.965 + 1.966 + if (aIsHorizontal) { 1.967 + pref -= (margin.left + margin.right); 1.968 + attribute = nsGkAtoms::width; 1.969 + } else { 1.970 + pref -= (margin.top + margin.bottom); 1.971 + attribute = nsGkAtoms::height; 1.972 + } 1.973 + 1.974 + nsIContent* content = aChildBox->GetContent(); 1.975 + 1.976 + // set its preferred size. 1.977 + nsAutoString prefValue; 1.978 + prefValue.AppendInt(pref/aOnePixel); 1.979 + if (content->AttrValueIs(kNameSpaceID_None, attribute, 1.980 + prefValue, eCaseMatters)) 1.981 + return; 1.982 + 1.983 + nsWeakFrame weakBox(aChildBox); 1.984 + content->SetAttr(kNameSpaceID_None, attribute, prefValue, true); 1.985 + ENSURE_TRUE(weakBox.IsAlive()); 1.986 + aState.PresShell()->FrameNeedsReflow(aChildBox, nsIPresShell::eStyleChange, 1.987 + NS_FRAME_IS_DIRTY); 1.988 +} 1.989 + 1.990 + 1.991 +void 1.992 +nsSplitterFrameInner::AddRemoveSpace(nscoord aDiff, 1.993 + nsSplitterInfo* aChildInfos, 1.994 + int32_t aCount, 1.995 + int32_t& aSpaceLeft) 1.996 +{ 1.997 + aSpaceLeft = 0; 1.998 + 1.999 + for (int i=0; i < aCount; i++) { 1.1000 + nscoord min = aChildInfos[i].min; 1.1001 + nscoord max = aChildInfos[i].max; 1.1002 + nscoord& c = aChildInfos[i].changed; 1.1003 + 1.1004 + // figure our how much space to add or remove 1.1005 + if (c + aDiff < min) { 1.1006 + aDiff += (c - min); 1.1007 + c = min; 1.1008 + } else if (c + aDiff > max) { 1.1009 + aDiff -= (max - c); 1.1010 + c = max; 1.1011 + } else { 1.1012 + c += aDiff; 1.1013 + aDiff = 0; 1.1014 + } 1.1015 + 1.1016 + // there is not space left? We are done 1.1017 + if (aDiff == 0) 1.1018 + break; 1.1019 + } 1.1020 + 1.1021 + aSpaceLeft = aDiff; 1.1022 +} 1.1023 + 1.1024 +/** 1.1025 + * Ok if we want to resize a child we will know the actual size in pixels we want it to be. 1.1026 + * This is not the preferred size. But they only way we can change a child is my manipulating its 1.1027 + * preferred size. So give the actual pixel size this return method will return figure out the preferred 1.1028 + * size and set it. 1.1029 + */ 1.1030 + 1.1031 +void 1.1032 +nsSplitterFrameInner::ResizeChildTo(nsPresContext* aPresContext, 1.1033 + nscoord& aDiff, 1.1034 + nsSplitterInfo* aChildrenBeforeInfos, 1.1035 + nsSplitterInfo* aChildrenAfterInfos, 1.1036 + int32_t aChildrenBeforeCount, 1.1037 + int32_t aChildrenAfterCount, 1.1038 + bool aBounded) 1.1039 +{ 1.1040 + nscoord spaceLeft; 1.1041 + AddRemoveSpace(aDiff, aChildrenBeforeInfos,aChildrenBeforeCount,spaceLeft); 1.1042 + 1.1043 + // if there is any space left over remove it from the dif we were originally given 1.1044 + aDiff -= spaceLeft; 1.1045 + AddRemoveSpace(-aDiff, aChildrenAfterInfos,aChildrenAfterCount,spaceLeft); 1.1046 + 1.1047 + if (spaceLeft != 0) { 1.1048 + if (aBounded) { 1.1049 + aDiff += spaceLeft; 1.1050 + AddRemoveSpace(spaceLeft, aChildrenBeforeInfos,aChildrenBeforeCount,spaceLeft); 1.1051 + } else { 1.1052 + spaceLeft = 0; 1.1053 + } 1.1054 + } 1.1055 +}