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