layout/xul/nsSplitterFrame.cpp

branch
TOR_BUG_9701
changeset 8
97036ab72558
equal deleted inserted replaced
-1:000000000000 0:19728f5184ab
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/. */
5
6 //
7 // Eric Vaughan
8 // Netscape Communications
9 //
10 // See documentation in associated header file
11 //
12
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"
39
40 using namespace mozilla;
41
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 };
52
53 class nsSplitterFrameInner : public nsIDOMEventListener
54 {
55 public:
56
57 NS_DECL_ISUPPORTS
58 NS_DECL_NSIDOMEVENTLISTENER
59
60 nsSplitterFrameInner(nsSplitterFrame* aSplitter)
61 {
62 mOuter = aSplitter;
63 mPressed = false;
64 }
65 virtual ~nsSplitterFrameInner();
66
67 void Disconnect() { mOuter = nullptr; }
68
69 nsresult MouseDown(nsIDOMEvent* aMouseEvent);
70 nsresult MouseUp(nsIDOMEvent* aMouseEvent);
71 nsresult MouseMove(nsIDOMEvent* aMouseEvent);
72
73 void MouseDrag(nsPresContext* aPresContext, WidgetGUIEvent* aEvent);
74 void MouseUp(nsPresContext* aPresContext, WidgetGUIEvent* aEvent);
75
76 void AdjustChildren(nsPresContext* aPresContext);
77 void AdjustChildren(nsPresContext* aPresContext, nsSplitterInfo* aChildInfos, int32_t aCount, bool aIsHorizontal);
78
79 void AddRemoveSpace(nscoord aDiff,
80 nsSplitterInfo* aChildInfos,
81 int32_t aCount,
82 int32_t& aSpaceLeft);
83
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);
91
92 void UpdateState();
93
94 void AddListener(nsPresContext* aPresContext);
95 void RemoveListener();
96
97 enum ResizeType { Closest, Farthest, Flex, Grow };
98 enum State { Open, CollapsedBefore, CollapsedAfter, Dragging };
99 enum CollapseDirection { Before, After };
100
101 ResizeType GetResizeBefore();
102 ResizeType GetResizeAfter();
103 State GetState();
104
105 void Reverse(nsSplitterInfo*& aIndexes, int32_t aCount);
106 bool SupportsCollapseDirection(CollapseDirection aDirection);
107
108 void EnsureOrient();
109 void SetPreferredSize(nsBoxLayoutState& aState, nsIFrame* aChildBox, nscoord aOnePixel, bool aIsHorizontal, nscoord* aSize);
110
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;
124
125 };
126
127 NS_IMPL_ISUPPORTS(nsSplitterFrameInner, nsIDOMEventListener)
128
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 }
142
143 nsSplitterFrameInner::~nsSplitterFrameInner()
144 {
145 delete[] mChildInfosBefore;
146 delete[] mChildInfosAfter;
147 }
148
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 }
163
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 }
190
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 }
201
202 NS_IMPL_FRAMEARENA_HELPERS(nsSplitterFrame)
203
204 nsSplitterFrame::nsSplitterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
205 : nsBoxFrame(aPresShell, aContext),
206 mInner(0)
207 {
208 }
209
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 }
221
222
223 nsresult
224 nsSplitterFrame::GetCursor(const nsPoint& aPoint,
225 nsIFrame::Cursor& aCursor)
226 {
227 return nsBoxFrame::GetCursor(aPoint, aCursor);
228
229 /*
230 if (IsHorizontal())
231 aCursor = NS_STYLE_CURSOR_N_RESIZE;
232 else
233 aCursor = NS_STYLE_CURSOR_W_RESIZE;
234
235 return NS_OK;
236 */
237 }
238
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 }
257
258 return rv;
259 }
260
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);
271
272 mInner->AddRef();
273 mInner->mChildInfosAfter = nullptr;
274 mInner->mChildInfosBefore = nullptr;
275 mInner->mState = nsSplitterFrameInner::Open;
276 mInner->mDragging = false;
277
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 }
295
296 nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
297
298 mInner->mState = nsSplitterFrameInner::Open;
299 mInner->AddListener(PresContext());
300 mInner->mParentBox = nullptr;
301 }
302
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 }
311
312 return nsBoxFrame::DoLayout(aState);
313 }
314
315
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 }
326
327 NS_IMETHODIMP
328 nsSplitterFrame::HandlePress(nsPresContext* aPresContext,
329 WidgetGUIEvent* aEvent,
330 nsEventStatus* aEventStatus)
331 {
332 return NS_OK;
333 }
334
335 NS_IMETHODIMP
336 nsSplitterFrame::HandleMultiplePress(nsPresContext* aPresContext,
337 WidgetGUIEvent* aEvent,
338 nsEventStatus* aEventStatus,
339 bool aControlHeld)
340 {
341 return NS_OK;
342 }
343
344 NS_IMETHODIMP
345 nsSplitterFrame::HandleDrag(nsPresContext* aPresContext,
346 WidgetGUIEvent* aEvent,
347 nsEventStatus* aEventStatus)
348 {
349 return NS_OK;
350 }
351
352 NS_IMETHODIMP
353 nsSplitterFrame::HandleRelease(nsPresContext* aPresContext,
354 WidgetGUIEvent* aEvent,
355 nsEventStatus* aEventStatus)
356 {
357 return NS_OK;
358 }
359
360 void
361 nsSplitterFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
362 const nsRect& aDirtyRect,
363 const nsDisplayListSet& aLists)
364 {
365 nsBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
366
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 }
376
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 }
386
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;
393
394 case NS_MOUSE_BUTTON_UP:
395 if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
396 mInner->MouseUp(aPresContext, aEvent);
397 }
398 break;
399 }
400
401 NS_ENSURE_STATE(weakFrame.IsAlive());
402 return nsBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
403 }
404
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);
418
419 mPressed = false;
420
421 // if we dragged then fire a command event.
422 if (mDidDrag) {
423 nsCOMPtr<nsIDOMXULElement> element = do_QueryInterface(mOuter->GetContent());
424 element->DoCommand();
425 }
426
427 //printf("MouseUp\n");
428 }
429
430 delete[] mChildInfosBefore;
431 delete[] mChildInfosAfter;
432 mChildInfosBefore = nullptr;
433 mChildInfosAfter = nullptr;
434 mChildInfosBeforeCount = 0;
435 mChildInfosAfterCount = 0;
436 }
437
438 void
439 nsSplitterFrameInner::MouseDrag(nsPresContext* aPresContext,
440 WidgetGUIEvent* aEvent)
441 {
442 if (mDragging && mOuter) {
443
444 //printf("Dragging\n");
445
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;
451
452 // mDragStart is in frame coordinates
453 nscoord start = mDragStart;
454
455 // take our current position and subtract the start location
456 pos -= start;
457
458 //printf("Diff=%d\n", pos);
459
460 ResizeType resizeAfter = GetResizeAfter();
461
462 bool bounded;
463
464 if (resizeAfter == nsSplitterFrameInner::Grow)
465 bounded = false;
466 else
467 bounded = true;
468
469 int i;
470 for (i=0; i < mChildInfosBeforeCount; i++)
471 mChildInfosBefore[i].changed = mChildInfosBefore[i].current;
472
473 for (i=0; i < mChildInfosAfterCount; i++)
474 mChildInfosAfter[i].changed = mChildInfosAfter[i].current;
475
476 nscoord oldPos = pos;
477
478 ResizeChildTo(aPresContext, pos, mChildInfosBefore, mChildInfosAfter, mChildInfosBeforeCount, mChildInfosAfterCount, bounded);
479
480 State currentState = GetState();
481 bool supportsBefore = SupportsCollapseDirection(Before);
482 bool supportsAfter = SupportsCollapseDirection(After);
483
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;
495
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 }
514
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 }
537
538 mDidDrag = true;
539 }
540 }
541
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 }
554
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 }
568
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);
581
582 NS_ABORT();
583 return NS_OK;
584 }
585
586 nsresult
587 nsSplitterFrameInner::MouseUp(nsIDOMEvent* aMouseEvent)
588 {
589 NS_ENSURE_TRUE(mOuter, NS_OK);
590 mPressed = false;
591
592 nsIPresShell::SetCapturingContent(nullptr, 0);
593
594 return NS_OK;
595 }
596
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;
604
605 int16_t button = 0;
606 mouseEvent->GetButton(&button);
607
608 // only if left button
609 if (button != 0)
610 return NS_OK;
611
612 if (mOuter->GetContent()->
613 AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
614 nsGkAtoms::_true, eCaseMatters))
615 return NS_OK;
616
617 mParentBox = mOuter->GetParentBox();
618 if (!mParentBox)
619 return NS_OK;
620
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;
629
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;
634
635 nsRefPtr<nsRenderingContext> rc =
636 outerPresContext->PresShell()->CreateReferenceRenderingContext();
637 nsBoxLayoutState state(outerPresContext, rc);
638 mCurrentPos = 0;
639 mPressed = true;
640
641 mDidDrag = false;
642
643 EnsureOrient();
644 bool isHorizontal = !mOuter->IsHorizontal();
645
646 ResizeType resizeBefore = GetResizeBefore();
647 ResizeType resizeAfter = GetResizeAfter();
648
649 delete[] mChildInfosBefore;
650 delete[] mChildInfosAfter;
651 mChildInfosBefore = new nsSplitterInfo[childCount];
652 mChildInfosAfter = new nsSplitterInfo[childCount];
653
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;
658
659 nsIFrame* childBox = mParentBox->GetChildBox();
660
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);
667
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);
674
675 mOuter->AddMargin(childBox, minSize);
676 mOuter->AddMargin(childBox, prefSize);
677 mOuter->AddMargin(childBox, maxSize);
678
679 nscoord flex = childBox->GetFlex(state);
680
681 nsMargin margin(0,0,0,0);
682 childBox->GetMargin(margin);
683 nsRect r(childBox->GetRect());
684 r.Inflate(margin);
685
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 }
713
714 childBox = childBox->GetNextBox();
715 count++;
716 }
717
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);
723
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 }
732
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);
737
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);
742
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;
747
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 }
758
759 mDragStart = c;
760
761 //printf("Pressed mDragStart=%d\n",mDragStart);
762
763 nsIPresShell::SetCapturingContent(mOuter->GetContent(), CAPTURE_IGNOREALLOWED);
764
765 return NS_OK;
766 }
767
768 nsresult
769 nsSplitterFrameInner::MouseMove(nsIDOMEvent* aMouseEvent)
770 {
771 NS_ENSURE_TRUE(mOuter, NS_OK);
772 if (!mPressed)
773 return NS_OK;
774
775 if (mDragging)
776 return NS_OK;
777
778 nsCOMPtr<nsIDOMEventListener> kungfuDeathGrip(this);
779 mOuter->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::state,
780 NS_LITERAL_STRING("dragging"), true);
781
782 RemoveListener();
783 mDragging = true;
784
785 return NS_OK;
786 }
787
788 void
789 nsSplitterFrameInner::Reverse(nsSplitterInfo*& aChildInfos, int32_t aCount)
790 {
791 nsSplitterInfo* infos = new nsSplitterInfo[aCount];
792
793 for (int i=0; i < aCount; i++)
794 infos[i] = aChildInfos[aCount - 1 - i];
795
796 delete[] aChildInfos;
797 aChildInfos = infos;
798 }
799
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};
808
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 }
819
820 return false;
821 }
822
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)
837
838 State newState = GetState();
839
840 if (newState == mState) {
841 // No change.
842 return;
843 }
844
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 }
854
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 }
879
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 }
889
890 void
891 nsSplitterFrameInner::AdjustChildren(nsPresContext* aPresContext)
892 {
893 EnsureOrient();
894 bool isHorizontal = !mOuter->IsHorizontal();
895
896 AdjustChildren(aPresContext, mChildInfosBefore, mChildInfosBeforeCount, isHorizontal);
897 AdjustChildren(aPresContext, mChildInfosAfter, mChildInfosAfterCount, isHorizontal);
898 }
899
900 static nsIFrame* GetChildBoxForContent(nsIFrame* aParentBox, nsIContent* aContent)
901 {
902 nsIFrame* childBox = aParentBox->GetChildBox();
903
904 while (nullptr != childBox) {
905 if (childBox->GetContent() == aContent) {
906 return childBox;
907 }
908 childBox = childBox->GetNextBox();
909 }
910 return nullptr;
911 }
912
913 void
914 nsSplitterFrameInner::AdjustChildren(nsPresContext* aPresContext, nsSplitterInfo* aChildInfos, int32_t aCount, bool aIsHorizontal)
915 {
916 ///printf("------- AdjustChildren------\n");
917
918 nsBoxLayoutState state(aPresContext);
919
920 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
921
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 }
929
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);
935
936 if (childBox) {
937 SetPreferredSize(state, childBox, onePixel, aIsHorizontal, &pref);
938 }
939 }
940 }
941
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;
947
948 if (!aSize)
949 {
950 if (aIsHorizontal)
951 pref = rect.width;
952 else
953 pref = rect.height;
954 } else {
955 pref = *aSize;
956 }
957
958 nsMargin margin(0,0,0,0);
959 aChildBox->GetMargin(margin);
960
961 nsCOMPtr<nsIAtom> attribute;
962
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 }
970
971 nsIContent* content = aChildBox->GetContent();
972
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;
979
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 }
986
987
988 void
989 nsSplitterFrameInner::AddRemoveSpace(nscoord aDiff,
990 nsSplitterInfo* aChildInfos,
991 int32_t aCount,
992 int32_t& aSpaceLeft)
993 {
994 aSpaceLeft = 0;
995
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;
1000
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;
1011 }
1012
1013 // there is not space left? We are done
1014 if (aDiff == 0)
1015 break;
1016 }
1017
1018 aSpaceLeft = aDiff;
1019 }
1020
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 */
1027
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)
1036 {
1037 nscoord spaceLeft;
1038 AddRemoveSpace(aDiff, aChildrenBeforeInfos,aChildrenBeforeCount,spaceLeft);
1039
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);
1043
1044 if (spaceLeft != 0) {
1045 if (aBounded) {
1046 aDiff += spaceLeft;
1047 AddRemoveSpace(spaceLeft, aChildrenBeforeInfos,aChildrenBeforeCount,spaceLeft);
1048 } else {
1049 spaceLeft = 0;
1050 }
1051 }
1052 }

mercurial