2 * Copyright 2010, Haiku Inc.
3 * Copyright 2006, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
4 * All rights reserved. Distributed under the terms of the MIT License.
14 #include <AutoDeleter.h>
15 #include <LayoutContext.h>
18 #include <ViewPrivate.h>
20 #include "ViewLayoutItem.h"
23 using BPrivate::AutoDeleter
;
30 // flags for our state
31 const uint32 B_LAYOUT_INVALID
= 0x80000000UL
; // needs layout
32 const uint32 B_LAYOUT_CACHE_INVALID
= 0x40000000UL
; // needs recalculation
33 const uint32 B_LAYOUT_REQUIRED
= 0x20000000UL
; // needs layout
34 const uint32 B_LAYOUT_IN_PROGRESS
= 0x10000000UL
;
35 const uint32 B_LAYOUT_ALL_CLEAR
= 0UL;
37 // handy masks to check various states
38 const uint32 B_LAYOUT_INVALIDATION_ILLEGAL
39 = B_LAYOUT_CACHE_INVALID
| B_LAYOUT_IN_PROGRESS
;
40 const uint32 B_LAYOUT_NECESSARY
41 = B_LAYOUT_INVALID
| B_LAYOUT_REQUIRED
| B_LAYOUT_CACHE_INVALID
;
42 const uint32 B_RELAYOUT_NOT_OK
43 = B_LAYOUT_INVALID
| B_LAYOUT_IN_PROGRESS
;
45 const char* const kLayoutItemField
= "BLayout:items";
49 inline void operator()(BView
* view
) {
51 BView::Private(view
).RemoveSelf();
59 fState(B_LAYOUT_ALL_CLEAR
),
60 fAncestorsVisible(true),
61 fInvalidationDisabled(0),
70 BLayout::BLayout(BMessage
* from
)
72 BLayoutItem(BUnarchiver::PrepareArchive(from
)),
73 fState(B_LAYOUT_ALL_CLEAR
),
74 fAncestorsVisible(true),
75 fInvalidationDisabled(0),
81 BUnarchiver
unarchiver(from
);
84 while (unarchiver
.EnsureUnarchived(kLayoutItemField
, i
++) == B_OK
)
91 // in case we have a view, but have been added to a layout as a BLayoutItem
92 // we will get deleted before our view, so we should tell it that we're
93 // going, so that we aren't double-freed.
94 if (fOwner
&& this == fOwner
->GetLayout())
95 fOwner
->_LayoutLeft(this);
97 if (CountItems() > 0) {
98 debugger("Deleting a BLayout that still has items. Subclass hooks "
99 "will not be called");
105 BLayout::Owner() const
112 BLayout::TargetView() const
126 BLayout::AddView(BView
* child
)
128 return AddView(-1, child
);
133 BLayout::AddView(int32 index
, BView
* child
)
135 BLayoutItem
* item
= child
->GetLayout();
136 ObjectDeleter
<BLayoutItem
> itemDeleter(NULL
);
138 item
= new(nothrow
) BViewLayoutItem(child
);
139 itemDeleter
.SetTo(item
);
142 if (item
&& AddItem(index
, item
)) {
143 itemDeleter
.Detach();
152 BLayout::AddItem(BLayoutItem
* item
)
154 return AddItem(-1, item
);
159 BLayout::AddItem(int32 index
, BLayoutItem
* item
)
161 if (!fTarget
|| !item
|| fItems
.HasItem(item
))
164 // if the item refers to a BView, we make sure it is added to the parent
166 BView
* view
= item
->View();
167 AutoDeleter
<BView
, ViewRemover
> remover(NULL
);
168 // In case of errors, we don't want to leave this view added where it
170 if (view
&& view
->fParent
!= fTarget
) {
171 if (!fTarget
->_AddChild(view
, NULL
))
177 // validate the index
178 if (index
< 0 || index
> fItems
.CountItems())
179 index
= fItems
.CountItems();
181 if (!fItems
.AddItem(item
, index
))
184 if (!ItemAdded(item
, index
)) {
185 fItems
.RemoveItem(index
);
189 item
->SetLayout(this);
190 if (!fAncestorsVisible
)
191 item
->AncestorVisibilityChanged(fAncestorsVisible
);
199 BLayout::RemoveView(BView
* child
)
201 bool removed
= false;
203 // a view can have any number of layout items - we need to remove them all
204 int32 remaining
= BView::Private(child
).CountLayoutItems();
205 for (int32 i
= CountItems() - 1; i
>= 0 && remaining
> 0; i
--) {
206 BLayoutItem
* item
= ItemAt(i
);
208 if (item
->View() != child
)
212 if (item
!= child
->GetLayout())
224 BLayout::RemoveItem(BLayoutItem
* item
)
226 int32 index
= IndexOfItem(item
);
227 return (index
>= 0 ? RemoveItem(index
) != NULL
: false);
232 BLayout::RemoveItem(int32 index
)
234 if (index
< 0 || index
>= fItems
.CountItems())
237 BLayoutItem
* item
= (BLayoutItem
*)fItems
.RemoveItem(index
);
238 ItemRemoved(item
, index
);
239 item
->SetLayout(NULL
);
241 // If this is the last item in use that refers to its BView,
242 // that BView now needs to be removed. UNLESS fTarget is NULL,
243 // in which case we leave the view as is. (See SetTarget() for more info)
244 BView
* view
= item
->View();
245 if (fTarget
&& view
&& BView::Private(view
).CountLayoutItems() == 0)
254 BLayout::ItemAt(int32 index
) const
256 return (BLayoutItem
*)fItems
.ItemAt(index
);
261 BLayout::CountItems() const
263 return fItems
.CountItems();
268 BLayout::IndexOfItem(const BLayoutItem
* item
) const
270 return fItems
.IndexOf(item
);
275 BLayout::IndexOfView(BView
* child
) const
280 // A BView can have many items, so we just do our best and return the
281 // index of the first one in this layout.
282 BView::Private
viewPrivate(child
);
283 int32 itemCount
= viewPrivate
.CountLayoutItems();
284 for (int32 i
= 0; i
< itemCount
; i
++) {
285 BLayoutItem
* item
= viewPrivate
.LayoutItemAt(i
);
286 if (item
->Layout() == this)
287 return IndexOfItem(item
);
294 BLayout::AncestorsVisible() const
296 return fAncestorsVisible
;
301 BLayout::InvalidateLayout(bool children
)
303 // printf("BLayout(%p)::InvalidateLayout(%i) : state %x, disabled %li\n",
304 // this, children, (unsigned int)fState, fInvalidationDisabled);
306 if (fTarget
&& fTarget
->IsLayoutInvalidationDisabled())
308 if (fInvalidationDisabled
> 0
309 || (fState
& B_LAYOUT_INVALIDATION_ILLEGAL
) != 0) {
313 fState
|= B_LAYOUT_NECESSARY
;
314 LayoutInvalidated(children
);
317 for (int32 i
= CountItems() - 1; i
>= 0; i
--)
318 ItemAt(i
)->InvalidateLayout(children
);
322 fOwner
->InvalidateLayout(children
);
324 if (BLayout
* nestedIn
= Layout()) {
325 nestedIn
->InvalidateLayout();
327 // If we weren't added as a BLayoutItem, we still have to invalidate
328 // whatever layout our owner is in.
329 fOwner
->_InvalidateParentLayout();
335 BLayout::RequireLayout()
337 fState
|= B_LAYOUT_REQUIRED
;
344 return (fState
& B_LAYOUT_INVALID
) == 0;
349 BLayout::DisableLayoutInvalidation()
351 fInvalidationDisabled
++;
356 BLayout::EnableLayoutInvalidation()
358 if (fInvalidationDisabled
> 0)
359 fInvalidationDisabled
--;
364 BLayout::LayoutItems(bool force
)
366 if ((fState
& B_LAYOUT_NECESSARY
) == 0 && !force
)
369 if (Layout() && (Layout()->fState
& B_LAYOUT_IN_PROGRESS
) != 0)
370 return; // wait for parent layout to lay us out.
372 if (fTarget
&& fTarget
->LayoutContext())
375 BLayoutContext context
;
376 _LayoutWithinContext(force
, &context
);
381 BLayout::Relayout(bool immediate
)
383 if ((fState
& B_RELAYOUT_NOT_OK
) == 0 || immediate
) {
384 fState
|= B_LAYOUT_REQUIRED
;
391 BLayout::_LayoutWithinContext(bool force
, BLayoutContext
* context
)
393 // printf("BLayout(%p)::_LayoutWithinContext(%i, %p), state %x, fContext %p\n",
394 // this, force, context, (unsigned int)fState, fContext);
396 if ((fState
& B_LAYOUT_NECESSARY
) == 0 && !force
)
399 BLayoutContext
* oldContext
= fContext
;
402 if (fOwner
&& BView::Private(fOwner
).WillLayout()) {
403 // in this case, let our owner decide whether or not to have us
404 // do our layout, if they do, we won't end up here again.
405 fOwner
->_Layout(force
, context
);
407 fState
|= B_LAYOUT_IN_PROGRESS
;
409 // we must ensure that all items are laid out, layouts with a view will
410 // have their layout process triggered by their view, but nested
411 // view-less layouts must have their layout triggered here (if it hasn't
412 // already been triggered).
413 int32 nestedLayoutCount
= fNestedLayouts
.CountItems();
414 for (int32 i
= 0; i
< nestedLayoutCount
; i
++) {
415 BLayout
* layout
= (BLayout
*)fNestedLayouts
.ItemAt(i
);
416 if ((layout
->fState
& B_LAYOUT_NECESSARY
) != 0)
417 layout
->_LayoutWithinContext(force
, context
);
419 fState
= B_LAYOUT_ALL_CLEAR
;
422 fContext
= oldContext
;
427 BLayout::LayoutArea()
431 area
.OffsetTo(B_ORIGIN
);
437 BLayout::Archive(BMessage
* into
, bool deep
) const
439 BArchiver
archiver(into
);
440 status_t err
= BLayoutItem::Archive(into
, deep
);
443 int32 count
= CountItems();
444 for (int32 i
= 0; i
< count
&& err
== B_OK
; i
++) {
445 BLayoutItem
* item
= ItemAt(i
);
446 err
= archiver
.AddArchivable(kLayoutItemField
, item
, deep
);
449 err
= ItemArchived(into
, item
, i
);
451 syslog(LOG_ERR
, "ItemArchived() failed at index: %d.", i
);
456 return archiver
.Finish(err
);
461 BLayout::AllArchived(BMessage
* archive
) const
463 return BLayoutItem::AllArchived(archive
);
468 BLayout::AllUnarchived(const BMessage
* from
)
470 BUnarchiver
unarchiver(from
);
471 status_t err
= BLayoutItem::AllUnarchived(from
);
476 unarchiver
.ArchiveMessage()->GetInfo(kLayoutItemField
, NULL
, &itemCount
);
477 for (int32 i
= 0; i
< itemCount
&& err
== B_OK
; i
++) {
479 err
= unarchiver
.FindObject(kLayoutItemField
,
480 i
, BUnarchiver::B_DONT_ASSUME_OWNERSHIP
, item
);
484 if (!fItems
.AddItem(item
, i
) || !ItemAdded(item
, i
)) {
485 fItems
.RemoveItem(i
);
489 err
= ItemUnarchived(from
, item
, i
);
491 fItems
.RemoveItem(i
);
492 ItemRemoved(item
, i
);
496 item
->SetLayout(this);
497 unarchiver
.AssumeOwnership(item
);
506 BLayout::ItemArchived(BMessage
* into
, BLayoutItem
* item
, int32 index
) const
513 BLayout::ItemUnarchived(const BMessage
* from
, BLayoutItem
* item
, int32 index
)
520 BLayout::ItemAdded(BLayoutItem
* item
, int32 atIndex
)
527 BLayout::ItemRemoved(BLayoutItem
* item
, int32 fromIndex
)
533 BLayout::LayoutInvalidated(bool children
)
539 BLayout::OwnerChanged(BView
* was
)
545 BLayout::AttachedToLayout()
548 Layout()->fNestedLayouts
.AddItem(this);
549 SetTarget(Layout()->TargetView());
555 BLayout::DetachedFromLayout(BLayout
* from
)
558 from
->fNestedLayouts
.RemoveItem(this);
565 BLayout::AncestorVisibilityChanged(bool shown
)
567 if (fAncestorsVisible
== shown
)
570 fAncestorsVisible
= shown
;
571 VisibilityChanged(shown
);
576 BLayout::VisibilityChanged(bool show
)
581 for (int32 i
= CountItems() - 1; i
>= 0; i
--)
582 ItemAt(i
)->AncestorVisibilityChanged(show
);
587 BLayout::ResetLayoutInvalidation()
589 fState
&= ~B_LAYOUT_CACHE_INVALID
;
594 BLayout::LayoutContext() const
601 BLayout::SetOwner(BView
* owner
)
615 BLayout::SetTarget(BView
* target
)
617 if (fTarget
!= target
) {
618 /* With fTarget NULL, RemoveItem() will not remove the views from their
619 * parent. This ensures that the views are not lost to the void.
623 // remove and delete all items
624 for (int32 i
= CountItems() - 1; i
>= 0; i
--)
625 delete RemoveItem(i
);
634 // Binary compatibility stuff
638 BLayout::Perform(perform_code code
, void* _data
)
640 return BLayoutItem::Perform(code
, _data
);
644 void BLayout::_ReservedLayout1() {}
645 void BLayout::_ReservedLayout2() {}
646 void BLayout::_ReservedLayout3() {}
647 void BLayout::_ReservedLayout4() {}
648 void BLayout::_ReservedLayout5() {}
649 void BLayout::_ReservedLayout6() {}
650 void BLayout::_ReservedLayout7() {}
651 void BLayout::_ReservedLayout8() {}
652 void BLayout::_ReservedLayout9() {}
653 void BLayout::_ReservedLayout10() {}