19ecf9d1cSIngo Weinhold/*
227d84b48SAdrien Destugues * Copyright 2010, Haiku Inc.
39ecf9d1cSIngo Weinhold * Copyright 2006, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
49ecf9d1cSIngo Weinhold * All rights reserved. Distributed under the terms of the MIT License.
59ecf9d1cSIngo Weinhold */
69ecf9d1cSIngo Weinhold
727d84b48SAdrien Destugues
89ecf9d1cSIngo Weinhold#include <Layout.h>
99ecf9d1cSIngo Weinhold
101d6c7b6cSAlex Wilson#include <algorithm>
119ecf9d1cSIngo Weinhold#include <new>
121d6c7b6cSAlex Wilson#include <syslog.h>
139ecf9d1cSIngo Weinhold
14fa01d084SAlex Wilson#include <AutoDeleter.h>
151d6c7b6cSAlex Wilson#include <LayoutContext.h>
1627d84b48SAdrien Destugues#include <Message.h>
179ecf9d1cSIngo Weinhold#include <View.h>
181d6c7b6cSAlex Wilson#include <ViewPrivate.h>
199ecf9d1cSIngo Weinhold
209ecf9d1cSIngo Weinhold#include "ViewLayoutItem.h"
219ecf9d1cSIngo Weinhold
2227d84b48SAdrien Destugues
2330b07d01SAlex Wilsonusing BPrivate::AutoDeleter;
2430b07d01SAlex Wilson
259ecf9d1cSIngo Weinholdusing std::nothrow;
261d6c7b6cSAlex Wilsonusing std::swap;
279ecf9d1cSIngo Weinhold
286829d417SAlex Wilson
2927d84b48SAdrien Destuguesnamespace {
301d6c7b6cSAlex Wilson	// flags for our state
311d6c7b6cSAlex Wilson	const uint32 B_LAYOUT_INVALID = 0x80000000UL; // needs layout
321d6c7b6cSAlex Wilson	const uint32 B_LAYOUT_CACHE_INVALID = 0x40000000UL; // needs recalculation
331d6c7b6cSAlex Wilson	const uint32 B_LAYOUT_REQUIRED = 0x20000000UL; // needs layout
341d6c7b6cSAlex Wilson	const uint32 B_LAYOUT_IN_PROGRESS = 0x10000000UL;
351d6c7b6cSAlex Wilson	const uint32 B_LAYOUT_ALL_CLEAR = 0UL;
361d6c7b6cSAlex Wilson
371d6c7b6cSAlex Wilson	// handy masks to check various states
381d6c7b6cSAlex Wilson	const uint32 B_LAYOUT_INVALIDATION_ILLEGAL
401d6c7b6cSAlex Wilson	const uint32 B_LAYOUT_NECESSARY
421d6c7b6cSAlex Wilson	const uint32 B_RELAYOUT_NOT_OK
441d6c7b6cSAlex Wilson
454a254e4dSAlex Wilson	const char* const kLayoutItemField = "BLayout:items";
46edb4c824SAlex Wilson
47edb4c824SAlex Wilson
48edb4c824SAlex Wilson	struct ViewRemover {
4930b07d01SAlex Wilson		inline void operator()(BView* view) {
50f87e4c73SAlex Wilson			if (view)
51f87e4c73SAlex Wilson				BView::Private(view).RemoveSelf();
52edb4c824SAlex Wilson		}
53edb4c824SAlex Wilson	};
5427d84b48SAdrien Destugues}
5527d84b48SAdrien Destugues
569ecf9d1cSIngo Weinhold
579ecf9d1cSIngo WeinholdBLayout::BLayout()
5827d84b48SAdrien Destugues	:
591d6c7b6cSAlex Wilson	fState(B_LAYOUT_ALL_CLEAR),
601d6c7b6cSAlex Wilson	fAncestorsVisible(true),
611d6c7b6cSAlex Wilson	fInvalidationDisabled(0),
621d6c7b6cSAlex Wilson	fContext(NULL),
631d6c7b6cSAlex Wilson	fOwner(NULL),
641d6c7b6cSAlex Wilson	fTarget(NULL),
6527d84b48SAdrien Destugues	fItems(20)
669ecf9d1cSIngo Weinhold{
679ecf9d1cSIngo Weinhold}
689ecf9d1cSIngo Weinhold
6927d84b48SAdrien Destugues
7027d84b48SAdrien DestuguesBLayout::BLayout(BMessage* from)
7127d84b48SAdrien Destugues	:
721d6c7b6cSAlex Wilson	BLayoutItem(BUnarchiver::PrepareArchive(from)),
731d6c7b6cSAlex Wilson	fState(B_LAYOUT_ALL_CLEAR),
741d6c7b6cSAlex Wilson	fAncestorsVisible(true),
751d6c7b6cSAlex Wilson	fInvalidationDisabled(0),
761d6c7b6cSAlex Wilson	fContext(NULL),
771d6c7b6cSAlex Wilson	fOwner(NULL),
781d6c7b6cSAlex Wilson	fTarget(NULL),
7927d84b48SAdrien Destugues	fItems(20)
8027d84b48SAdrien Destugues{
8127d84b48SAdrien Destugues	BUnarchiver unarchiver(from);
8227d84b48SAdrien Destugues
8327d84b48SAdrien Destugues	int32 i = 0;
8427d84b48SAdrien Destugues	while (unarchiver.EnsureUnarchived(kLayoutItemField, i++) == B_OK)
8527d84b48SAdrien Destugues		;
8627d84b48SAdrien Destugues}
8727d84b48SAdrien Destugues
8827d84b48SAdrien Destugues
899ecf9d1cSIngo WeinholdBLayout::~BLayout()
909ecf9d1cSIngo Weinhold{
911d6c7b6cSAlex Wilson	// in case we have a view, but have been added to a layout as a BLayoutItem
921d6c7b6cSAlex Wilson	// we will get deleted before our view, so we should tell it that we're
931d6c7b6cSAlex Wilson	// going, so that we aren't double-freed.
941d6c7b6cSAlex Wilson	if (fOwner && this == fOwner->GetLayout())
951d6c7b6cSAlex Wilson		fOwner->_LayoutLeft(this);
961d6c7b6cSAlex Wilson
97e837ee8bSMichael Lotz	if (CountItems() > 0) {
98e837ee8bSMichael Lotz		debugger("Deleting a BLayout that still has items. Subclass hooks "
99e837ee8bSMichael Lotz			"will not be called");
100e837ee8bSMichael Lotz	}
1011d6c7b6cSAlex Wilson}
1021d6c7b6cSAlex Wilson
1031d6c7b6cSAlex Wilson
1041d6c7b6cSAlex WilsonBView*
1051d6c7b6cSAlex WilsonBLayout::Owner() const
1061d6c7b6cSAlex Wilson{
1071d6c7b6cSAlex Wilson	return fOwner;
1089ecf9d1cSIngo Weinhold}
1099ecf9d1cSIngo Weinhold
11027d84b48SAdrien Destugues
1119ecf9d1cSIngo WeinholdBView*
1121d6c7b6cSAlex WilsonBLayout::TargetView() const
1139ecf9d1cSIngo Weinhold{
1141d6c7b6cSAlex Wilson	return fTarget;
1151d6c7b6cSAlex Wilson}
1161d6c7b6cSAlex Wilson
1171d6c7b6cSAlex Wilson
1181d6c7b6cSAlex WilsonBView*
1191d6c7b6cSAlex WilsonBLayout::View()
1201d6c7b6cSAlex Wilson{
1211d6c7b6cSAlex Wilson	return fOwner;
1229ecf9d1cSIngo Weinhold}
1239ecf9d1cSIngo Weinhold
12427d84b48SAdrien Destugues
1259ecf9d1cSIngo WeinholdBLayoutItem*
1269ecf9d1cSIngo WeinholdBLayout::AddView(BView* child)
1279ecf9d1cSIngo Weinhold{
1289ecf9d1cSIngo Weinhold	return AddView(-1, child);
1299ecf9d1cSIngo Weinhold}
1309ecf9d1cSIngo Weinhold
13127d84b48SAdrien Destugues
1329ecf9d1cSIngo WeinholdBLayoutItem*
1339ecf9d1cSIngo WeinholdBLayout::AddView(int32 index, BView* child)
1349ecf9d1cSIngo Weinhold{
1351d6c7b6cSAlex Wilson	BLayoutItem* item = child->GetLayout();
13630b07d01SAlex Wilson	ObjectDeleter<BLayoutItem> itemDeleter(NULL);
13730b07d01SAlex Wilson	if (!item) {
1381d6c7b6cSAlex Wilson		item = new(nothrow) BViewLayoutItem(child);
13930b07d01SAlex Wilson		itemDeleter.SetTo(item);
14030b07d01SAlex Wilson	}
1411d6c7b6cSAlex Wilson
14230b07d01SAlex Wilson	if (item && AddItem(index, item)) {
14330b07d01SAlex Wilson		itemDeleter.Detach();
1441d6c7b6cSAlex Wilson		return item;
14530b07d01SAlex Wilson	}
1461d6c7b6cSAlex Wilson
1479ecf9d1cSIngo Weinhold	return NULL;
1489ecf9d1cSIngo Weinhold}
1499ecf9d1cSIngo Weinhold
15027d84b48SAdrien Destugues
1519ecf9d1cSIngo Weinholdbool
1529ecf9d1cSIngo WeinholdBLayout::AddItem(BLayoutItem* item)
1539ecf9d1cSIngo Weinhold{
1549ecf9d1cSIngo Weinhold	return AddItem(-1, item);
1559ecf9d1cSIngo Weinhold}
1569ecf9d1cSIngo Weinhold
15727d84b48SAdrien Destugues
1589ecf9d1cSIngo Weinholdbool
1599ecf9d1cSIngo WeinholdBLayout::AddItem(int32 index, BLayoutItem* item)
1609ecf9d1cSIngo Weinhold{
1611d6c7b6cSAlex Wilson	if (!fTarget || !item || fItems.HasItem(item))
1629ecf9d1cSIngo Weinhold		return false;
163f374f0d9SAxel Dörfler
16427d84b48SAdrien Destugues	// if the item refers to a BView, we make sure it is added to the parent
1659ecf9d1cSIngo Weinhold	// view
1669ecf9d1cSIngo Weinhold	BView* view = item->View();
16790e61567SAlex Wilson	AutoDeleter<BView, ViewRemover> remover(NULL);
16890e61567SAlex Wilson		// In case of errors, we don't want to leave this view added where it
16990e61567SAlex Wilson		// shouldn't be.
17090e61567SAlex Wilson	if (view && view->fParent != fTarget) {
17190e61567SAlex Wilson		if (!fTarget->_AddChild(view, NULL))
17290e61567SAlex Wilson			return false;
17390e61567SAlex Wilson		else
17490e61567SAlex Wilson			remover.SetTo(view);
175edb4c824SAlex Wilson	}
1769ecf9d1cSIngo Weinhold
1779ecf9d1cSIngo Weinhold	// validate the index
1789ecf9d1cSIngo Weinhold	if (index < 0 || index > fItems.CountItems())
1799ecf9d1cSIngo Weinhold		index = fItems.CountItems();
1809ecf9d1cSIngo Weinhold
181edb4c824SAlex Wilson	if (!fItems.AddItem(item, index))
182edb4c824SAlex Wilson		return false;
183edb4c824SAlex Wilson
184edb4c824SAlex Wilson	if (!ItemAdded(item, index)) {
185edb4c824SAlex Wilson		fItems.RemoveItem(index);
1866829d417SAlex Wilson		return false;
1876829d417SAlex Wilson	}
188edb4c824SAlex Wilson
189edb4c824SAlex Wilson	item->SetLayout(this);
190edb4c824SAlex Wilson	if (!fAncestorsVisible)
191edb4c824SAlex Wilson		item->AncestorVisibilityChanged(fAncestorsVisible);
192edb4c824SAlex Wilson	InvalidateLayout();
193edb4c824SAlex Wilson	remover.Detach();
194edb4c824SAlex Wilson	return true;
1959ecf9d1cSIngo Weinhold}
1969ecf9d1cSIngo Weinhold
19727d84b48SAdrien Destugues
1989ecf9d1cSIngo Weinholdbool
1999ecf9d1cSIngo WeinholdBLayout::RemoveView(BView* child)
2009ecf9d1cSIngo Weinhold{
201f374f0d9SAxel Dörfler	bool removed = false;
202f374f0d9SAxel Dörfler
203f374f0d9SAxel Dörfler	// a view can have any number of layout items - we need to remove them all
204f1e81e61SAlex Wilson	int32 remaining = BView::Private(child).CountLayoutItems();
205f1e81e61SAlex Wilson	for (int32 i = CountItems() - 1; i >= 0 && remaining > 0; i--) {
206f1e81e61SAlex Wilson		BLayoutItem* item = ItemAt(i);
207f374f0d9SAxel Dörfler
208f1e81e61SAlex Wilson		if (item->View() != child)
209f374f0d9SAxel Dörfler			continue;
210f374f0d9SAxel Dörfler
211f374f0d9SAxel Dörfler		RemoveItem(i);
212ebea950bSMichael Lotz		if (item != child->GetLayout())
213ebea950bSMichael Lotz			delete item;
214f1e81e61SAlex Wilson
215f1e81e61SAlex Wilson		remaining--;
216f1e81e61SAlex Wilson		removed = true;
2179ecf9d1cSIngo Weinhold	}
2189ecf9d1cSIngo Weinhold
219f374f0d9SAxel Dörfler	return removed;
2209ecf9d1cSIngo Weinhold}
2219ecf9d1cSIngo Weinhold
22227d84b48SAdrien Destugues
2239ecf9d1cSIngo Weinholdbool
2249ecf9d1cSIngo WeinholdBLayout::RemoveItem(BLayoutItem* item)
2259ecf9d1cSIngo Weinhold{
2269ecf9d1cSIngo Weinhold	int32 index = IndexOfItem(item);
227caddc641SJoseph R. Prostko	return (index >= 0 ? RemoveItem(index) != NULL : false);
2289ecf9d1cSIngo Weinhold}
2299ecf9d1cSIngo Weinhold
23027d84b48SAdrien Destugues
2319ecf9d1cSIngo WeinholdBLayoutItem*
2329ecf9d1cSIngo WeinholdBLayout::RemoveItem(int32 index)
2339ecf9d1cSIngo Weinhold{
2349ecf9d1cSIngo Weinhold	if (index < 0 || index >= fItems.CountItems())
2359ecf9d1cSIngo Weinhold		return NULL;
2369ecf9d1cSIngo Weinhold
2379ecf9d1cSIngo Weinhold	BLayoutItem* item = (BLayoutItem*)fItems.RemoveItem(index);
238cc1ca167SAlex Wilson	ItemRemoved(item, index);
239cc1ca167SAlex Wilson	item->SetLayout(NULL);
240cc1ca167SAlex Wilson
241cc1ca167SAlex Wilson	// If this is the last item in use that refers to its BView,
242cc1ca167SAlex Wilson	// that BView now needs to be removed. UNLESS fTarget is NULL,
243cc1ca167SAlex Wilson	// in which case we leave the view as is. (See SetTarget() for more info)
2449ecf9d1cSIngo Weinhold	BView* view = item->View();
245cc1ca167SAlex Wilson	if (fTarget && view && BView::Private(view).CountLayoutItems() == 0)
2469ecf9d1cSIngo Weinhold		view->_RemoveSelf();
2479ecf9d1cSIngo Weinhold
2489ecf9d1cSIngo Weinhold	InvalidateLayout();
2499ecf9d1cSIngo Weinhold	return item;
2509ecf9d1cSIngo Weinhold}
2519ecf9d1cSIngo Weinhold
25227d84b48SAdrien Destugues
2539ecf9d1cSIngo WeinholdBLayoutItem*
2549ecf9d1cSIngo WeinholdBLayout::ItemAt(int32 index) const
2559ecf9d1cSIngo Weinhold{
2569ecf9d1cSIngo Weinhold	return (BLayoutItem*)fItems.ItemAt(index);
2579ecf9d1cSIngo Weinhold}
2589ecf9d1cSIngo Weinhold
25927d84b48SAdrien Destugues
2609ecf9d1cSIngo Weinholdint32
2619ecf9d1cSIngo WeinholdBLayout::CountItems() const
2629ecf9d1cSIngo Weinhold{
2639ecf9d1cSIngo Weinhold	return fItems.CountItems();
2649ecf9d1cSIngo Weinhold}
2659ecf9d1cSIngo Weinhold
26627d84b48SAdrien Destugues
2679ecf9d1cSIngo Weinholdint32
26827d84b48SAdrien DestuguesBLayout::IndexOfItem(const BLayoutItem* item) const
2699ecf9d1cSIngo Weinhold{
2709ecf9d1cSIngo Weinhold	return fItems.IndexOf(item);
2719ecf9d1cSIngo Weinhold}
2729ecf9d1cSIngo Weinhold
27327d84b48SAdrien Destugues
2749ecf9d1cSIngo Weinholdint32
2759ecf9d1cSIngo WeinholdBLayout::IndexOfView(BView* child) const
2769ecf9d1cSIngo Weinhold{
2773ea301f3SAlex Wilson	if (child == NULL)
2783ea301f3SAlex Wilson		return -1;
2793ea301f3SAlex Wilson
280ea907ab5SAlex Wilson	// A BView can have many items, so we just do our best and return the
281ea907ab5SAlex Wilson	// index of the first one in this layout.
282ea907ab5SAlex Wilson	BView::Private viewPrivate(child);
283ea907ab5SAlex Wilson	int32 itemCount = viewPrivate.CountLayoutItems();
2849ecf9d1cSIngo Weinhold	for (int32 i = 0; i < itemCount; i++) {
285ea907ab5SAlex Wilson		BLayoutItem* item = viewPrivate.LayoutItemAt(i);
286ea907ab5SAlex Wilson		if (item->Layout() == this)
287ea907ab5SAlex Wilson			return IndexOfItem(item);
2889ecf9d1cSIngo Weinhold	}
2899ecf9d1cSIngo Weinhold	return -1;
2909ecf9d1cSIngo Weinhold}
2919ecf9d1cSIngo Weinhold
29227d84b48SAdrien Destugues
2931d6c7b6cSAlex Wilsonbool
294df730987SIngo WeinholdBLayout::AncestorsVisible() const
2951d6c7b6cSAlex Wilson{
2961d6c7b6cSAlex Wilson	return fAncestorsVisible;
2971d6c7b6cSAlex Wilson}
2981d6c7b6cSAlex Wilson
2991d6c7b6cSAlex Wilson
3009ecf9d1cSIngo Weinholdvoid
3011d6c7b6cSAlex WilsonBLayout::InvalidateLayout(bool children)
3021d6c7b6cSAlex Wilson{
3031d6c7b6cSAlex Wilson	// printf("BLayout(%p)::InvalidateLayout(%i) : state %x, disabled %li\n",
3041d6c7b6cSAlex Wilson	// this, children, (unsigned int)fState, fInvalidationDisabled);
3051d6c7b6cSAlex Wilson
30631f76857Sczeidler	if (fTarget && fTarget->IsLayoutInvalidationDisabled())
30731f76857Sczeidler		return;
308e7b0dc78SAlex Wilson	if (fInvalidationDisabled > 0
309e7b0dc78SAlex Wilson		|| (fState & B_LAYOUT_INVALIDATION_ILLEGAL) != 0) {
3101d6c7b6cSAlex Wilson		return;
311e7b0dc78SAlex Wilson	}
3121d6c7b6cSAlex Wilson
3131d6c7b6cSAlex Wilson	fState |= B_LAYOUT_NECESSARY;
314b38ea98fSAlex Wilson	LayoutInvalidated(children);
3151d6c7b6cSAlex Wilson
3161d6c7b6cSAlex Wilson	if (children) {
3171d6c7b6cSAlex Wilson		for (int32 i = CountItems() - 1; i >= 0; i--)
318df730987SIngo Weinhold			ItemAt(i)->InvalidateLayout(children);
3191d6c7b6cSAlex Wilson	}
3201d6c7b6cSAlex Wilson
321e7b0dc78SAlex Wilson	if (fOwner)
3221d6c7b6cSAlex Wilson		fOwner->InvalidateLayout(children);
3231d6c7b6cSAlex Wilson
3241d6c7b6cSAlex Wilson	if (BLayout* nestedIn = Layout()) {
325e7b0dc78SAlex Wilson		nestedIn->InvalidateLayout();
3261d6c7b6cSAlex Wilson	} else if (fOwner) {
3271d6c7b6cSAlex Wilson		// If we weren't added as a BLayoutItem, we still have to invalidate
3281d6c7b6cSAlex Wilson		// whatever layout our owner is in.
329e7b0dc78SAlex Wilson		fOwner->_InvalidateParentLayout();
3301d6c7b6cSAlex Wilson	}
3311d6c7b6cSAlex Wilson}
3321d6c7b6cSAlex Wilson
3331d6c7b6cSAlex Wilson
3341d6c7b6cSAlex Wilsonvoid
3351d6c7b6cSAlex WilsonBLayout::RequireLayout()
3361d6c7b6cSAlex Wilson{
3371d6c7b6cSAlex Wilson	fState |= B_LAYOUT_REQUIRED;
3381d6c7b6cSAlex Wilson}
3391d6c7b6cSAlex Wilson
3401d6c7b6cSAlex Wilson
3411d6c7b6cSAlex Wilsonbool
3421d6c7b6cSAlex WilsonBLayout::IsValid()
3431d6c7b6cSAlex Wilson{
3441d6c7b6cSAlex Wilson	return (fState & B_LAYOUT_INVALID) == 0;
3451d6c7b6cSAlex Wilson}
3461d6c7b6cSAlex Wilson
3471d6c7b6cSAlex Wilson
3481d6c7b6cSAlex Wilsonvoid
3491d6c7b6cSAlex WilsonBLayout::DisableLayoutInvalidation()
3501d6c7b6cSAlex Wilson{
3511d6c7b6cSAlex Wilson	fInvalidationDisabled++;
3521d6c7b6cSAlex Wilson}
3531d6c7b6cSAlex Wilson
3541d6c7b6cSAlex Wilson
3551d6c7b6cSAlex Wilsonvoid
3561d6c7b6cSAlex WilsonBLayout::EnableLayoutInvalidation()
3571d6c7b6cSAlex Wilson{
3581d6c7b6cSAlex Wilson	if (fInvalidationDisabled > 0)
3591d6c7b6cSAlex Wilson		fInvalidationDisabled--;
3601d6c7b6cSAlex Wilson}
3611d6c7b6cSAlex Wilson
3621d6c7b6cSAlex Wilson
3631d6c7b6cSAlex Wilsonvoid
3641d6c7b6cSAlex WilsonBLayout::LayoutItems(bool force)
3651d6c7b6cSAlex Wilson{
3661d6c7b6cSAlex Wilson	if ((fState & B_LAYOUT_NECESSARY) == 0 && !force)
3671d6c7b6cSAlex Wilson		return;
3681d6c7b6cSAlex Wilson
3691d6c7b6cSAlex Wilson	if (Layout() && (Layout()->fState & B_LAYOUT_IN_PROGRESS) != 0)
3701d6c7b6cSAlex Wilson		return; // wait for parent layout to lay us out.
3711d6c7b6cSAlex Wilson
3721d6c7b6cSAlex Wilson	if (fTarget && fTarget->LayoutContext())
3731d6c7b6cSAlex Wilson		return;
3741d6c7b6cSAlex Wilson
3751d6c7b6cSAlex Wilson	BLayoutContext context;
3761d6c7b6cSAlex Wilson	_LayoutWithinContext(force, &context);
3771d6c7b6cSAlex Wilson}
3781d6c7b6cSAlex Wilson
3791d6c7b6cSAlex Wilson
3801d6c7b6cSAlex Wilsonvoid
3811d6c7b6cSAlex WilsonBLayout::Relayout(bool immediate)
3821d6c7b6cSAlex Wilson{
3831d6c7b6cSAlex Wilson	if ((fState & B_RELAYOUT_NOT_OK) == 0 || immediate) {
3841d6c7b6cSAlex Wilson		fState |= B_LAYOUT_REQUIRED;
3851d6c7b6cSAlex Wilson		LayoutItems(false);
3861d6c7b6cSAlex Wilson	}
3871d6c7b6cSAlex Wilson}
3881d6c7b6cSAlex Wilson
3891d6c7b6cSAlex Wilson
3901d6c7b6cSAlex Wilsonvoid
3911d6c7b6cSAlex WilsonBLayout::_LayoutWithinContext(bool force, BLayoutContext* context)
3921d6c7b6cSAlex Wilson{
3931d6c7b6cSAlex Wilson// printf("BLayout(%p)::_LayoutWithinContext(%i, %p), state %x, fContext %p\n",
3941d6c7b6cSAlex Wilson// this, force, context, (unsigned int)fState, fContext);
3951d6c7b6cSAlex Wilson
3961d6c7b6cSAlex Wilson	if ((fState & B_LAYOUT_NECESSARY) == 0 && !force)
3971d6c7b6cSAlex Wilson		return;
3981d6c7b6cSAlex Wilson
3991d6c7b6cSAlex Wilson	BLayoutContext* oldContext = fContext;
4001d6c7b6cSAlex Wilson	fContext = context;
4011d6c7b6cSAlex Wilson
4021d6c7b6cSAlex Wilson	if (fOwner && BView::Private(fOwner).WillLayout()) {
4031d6c7b6cSAlex Wilson		// in this case, let our owner decide whether or not to have us
4041d6c7b6cSAlex Wilson		// do our layout, if they do, we won't end up here again.
4051d6c7b6cSAlex Wilson		fOwner->_Layout(force, context);
4061d6c7b6cSAlex Wilson	} else {
4071d6c7b6cSAlex Wilson		fState |= B_LAYOUT_IN_PROGRESS;
4084e0131f5SAlex Wilson		DoLayout();
4091d6c7b6cSAlex Wilson		// we must ensure that all items are laid out, layouts with a view will
4101d6c7b6cSAlex Wilson		// have their layout process triggered by their view, but nested
4111d6c7b6cSAlex Wilson		// view-less layouts must have their layout triggered here (if it hasn't
4121d6c7b6cSAlex Wilson		// already been triggered).
4131d6c7b6cSAlex Wilson		int32 nestedLayoutCount = fNestedLayouts.CountItems();
4141d6c7b6cSAlex Wilson		for (int32 i = 0; i < nestedLayoutCount; i++) {
415df730987SIngo Weinhold			BLayout* layout = (BLayout*)fNestedLayouts.ItemAt(i);
4161d6c7b6cSAlex Wilson			if ((layout->fState & B_LAYOUT_NECESSARY) != 0)
4171d6c7b6cSAlex Wilson				layout->_LayoutWithinContext(force, context);
4181d6c7b6cSAlex Wilson		}
4191d6c7b6cSAlex Wilson		fState = B_LAYOUT_ALL_CLEAR;
4201d6c7b6cSAlex Wilson	}
4211d6c7b6cSAlex Wilson
4221d6c7b6cSAlex Wilson	fContext = oldContext;
4231d6c7b6cSAlex Wilson}
4241d6c7b6cSAlex Wilson
4251d6c7b6cSAlex Wilson
4261d6c7b6cSAlex WilsonBRect
4271d6c7b6cSAlex WilsonBLayout::LayoutArea()
4289ecf9d1cSIngo Weinhold{
4291d6c7b6cSAlex Wilson	BRect area(Frame());
4301d6c7b6cSAlex Wilson	if (fOwner)
4311d6c7b6cSAlex Wilson		area.OffsetTo(B_ORIGIN);
4321d6c7b6cSAlex Wilson	return area;
4339ecf9d1cSIngo Weinhold}
4349ecf9d1cSIngo Weinhold
43527d84b48SAdrien Destugues
43627d84b48SAdrien Destuguesstatus_t
43727d84b48SAdrien DestuguesBLayout::Archive(BMessage* into, bool deep) const
43827d84b48SAdrien Destugues{
43927d84b48SAdrien Destugues	BArchiver archiver(into);
4401d6c7b6cSAlex Wilson	status_t err = BLayoutItem::Archive(into, deep);
44127d84b48SAdrien Destugues
44227d84b48SAdrien Destugues	if (deep) {
44327d84b48SAdrien Destugues		int32 count = CountItems();
444b137ab3eSIngo Weinhold		for (int32 i = 0; i < count && err == B_OK; i++) {
445b137ab3eSIngo Weinhold			BLayoutItem* item = ItemAt(i);
446b137ab3eSIngo Weinhold			err = archiver.AddArchivable(kLayoutItemField, item, deep);
44727d84b48SAdrien Destugues
44827d84b48SAdrien Destugues			if (err == B_OK) {
449b137ab3eSIngo Weinhold				err = ItemArchived(into, item, i);
450b137ab3eSIngo Weinhold				if (err != B_OK)
451b137ab3eSIngo Weinhold					syslog(LOG_ERR, "ItemArchived() failed at index: %d.", i);
45227d84b48SAdrien Destugues			}
45327d84b48SAdrien Destugues		}
45427d84b48SAdrien Destugues	}
45527d84b48SAdrien Destugues
456b137ab3eSIngo Weinhold	return archiver.Finish(err);
45727d84b48SAdrien Destugues}
45827d84b48SAdrien Destugues
45927d84b48SAdrien Destugues
46053617d36SAlex Wilsonstatus_t
46153617d36SAlex WilsonBLayout::AllArchived(BMessage* archive) const
46253617d36SAlex Wilson{
4639f029231SAlex Wilson	return BLayoutItem::AllArchived(archive);
46453617d36SAlex Wilson}
46553617d36SAlex Wilson
46653617d36SAlex Wilson
46727d84b48SAdrien Destuguesstatus_t
46827d84b48SAdrien DestuguesBLayout::AllUnarchived(const BMessage* from)
46927d84b48SAdrien Destugues{
47027d84b48SAdrien Destugues	BUnarchiver unarchiver(from);
4711d6c7b6cSAlex Wilson	status_t err = BLayoutItem::AllUnarchived(from);
47227d84b48SAdrien Destugues	if (err != B_OK)
47327d84b48SAdrien Destugues		return err;
47427d84b48SAdrien Destugues
475981c729bSAlex Wilson	int32 itemCount = 0;
476b137ab3eSIngo Weinhold	unarchiver.ArchiveMessage()->GetInfo(kLayoutItemField, NULL, &itemCount);
477b137ab3eSIngo Weinhold	for (int32 i = 0; i < itemCount && err == B_OK; i++) {
478b137ab3eSIngo Weinhold		BLayoutItem* item;
4796829d417SAlex Wilson		err = unarchiver.FindObject(kLayoutItemField,
4806829d417SAlex Wilson			i, BUnarchiver::B_DONT_ASSUME_OWNERSHIP, item);
4816829d417SAlex Wilson		if (err != B_OK)
4826829d417SAlex Wilson			return err;
4836829d417SAlex Wilson
4846829d417SAlex Wilson		if (!fItems.AddItem(item, i) || !ItemAdded(item, i)) {
4856829d417SAlex Wilson			fItems.RemoveItem(i);
4866829d417SAlex Wilson			return B_ERROR;
48727d84b48SAdrien Destugues		}
4886829d417SAlex Wilson
4896829d417SAlex Wilson		err = ItemUnarchived(from, item, i);
4906829d417SAlex Wilson		if (err != B_OK) {
4916829d417SAlex Wilson			fItems.RemoveItem(i);
492df730987SIngo Weinhold			ItemRemoved(item, i);
4936829d417SAlex Wilson			return err;
4946829d417SAlex Wilson		}
4956829d417SAlex Wilson
4966829d417SAlex Wilson		item->SetLayout(this);
4976829d417SAlex Wilson		unarchiver.AssumeOwnership(item);
49827d84b48SAdrien Destugues	}
49927d84b48SAdrien Destugues
5006829d417SAlex Wilson	InvalidateLayout();
501b137ab3eSIngo Weinhold	return err;
50227d84b48SAdrien Destugues}
50327d84b48SAdrien Destugues
50427d84b48SAdrien Destugues
50527d84b48SAdrien Destuguesstatus_t
5066829d417SAlex WilsonBLayout::ItemArchived(BMessage* into, BLayoutItem* item, int32 index) const
50727d84b48SAdrien Destugues{
50827d84b48SAdrien Destugues	return B_OK;
50927d84b48SAdrien Destugues}
51027d84b48SAdrien Destugues
51127d84b48SAdrien Destugues
51227d84b48SAdrien Destuguesstatus_t
513b137ab3eSIngo WeinholdBLayout::ItemUnarchived(const BMessage* from, BLayoutItem* item, int32 index)
51427d84b48SAdrien Destugues{
515b137ab3eSIngo Weinhold	return B_OK;
51627d84b48SAdrien Destugues}
51727d84b48SAdrien Destugues
51827d84b48SAdrien Destugues
5196829d417SAlex Wilsonbool
5206829d417SAlex WilsonBLayout::ItemAdded(BLayoutItem* item, int32 atIndex)
5219ecf9d1cSIngo Weinhold{
5226829d417SAlex Wilson	return true;
5239ecf9d1cSIngo Weinhold}
5249ecf9d1cSIngo Weinhold
52527d84b48SAdrien Destugues
5269ecf9d1cSIngo Weinholdvoid
5276829d417SAlex WilsonBLayout::ItemRemoved(BLayoutItem* item, int32 fromIndex)
5289ecf9d1cSIngo Weinhold{
5299ecf9d1cSIngo Weinhold}
5309ecf9d1cSIngo Weinhold
53127d84b48SAdrien Destugues
532b38ea98fSAlex Wilsonvoid
533b38ea98fSAlex WilsonBLayout::LayoutInvalidated(bool children)
534b38ea98fSAlex Wilson{
535b38ea98fSAlex Wilson}
536b38ea98fSAlex Wilson
537b38ea98fSAlex Wilson
5389ecf9d1cSIngo Weinholdvoid
5391d6c7b6cSAlex WilsonBLayout::OwnerChanged(BView* was)
5401d6c7b6cSAlex Wilson{
5411d6c7b6cSAlex Wilson}
5421d6c7b6cSAlex Wilson
5431d6c7b6cSAlex Wilson
5441d6c7b6cSAlex Wilsonvoid
5451d6c7b6cSAlex WilsonBLayout::AttachedToLayout()
5461d6c7b6cSAlex Wilson{
5471d6c7b6cSAlex Wilson	if (!fOwner) {
5481d6c7b6cSAlex Wilson		Layout()->fNestedLayouts.AddItem(this);
5491d6c7b6cSAlex Wilson		SetTarget(Layout()->TargetView());
5501d6c7b6cSAlex Wilson	}
5511d6c7b6cSAlex Wilson}
5521d6c7b6cSAlex Wilson
5531d6c7b6cSAlex Wilson
5541d6c7b6cSAlex Wilsonvoid
5551d6c7b6cSAlex WilsonBLayout::DetachedFromLayout(BLayout* from)
5561d6c7b6cSAlex Wilson{
5571d6c7b6cSAlex Wilson	if (!fOwner) {
5581d6c7b6cSAlex Wilson		from->fNestedLayouts.RemoveItem(this);
5591d6c7b6cSAlex Wilson		SetTarget(NULL);
5601d6c7b6cSAlex Wilson	}
5611d6c7b6cSAlex Wilson}
5621d6c7b6cSAlex Wilson
5631d6c7b6cSAlex Wilson
5641d6c7b6cSAlex Wilsonvoid
5651d6c7b6cSAlex WilsonBLayout::AncestorVisibilityChanged(bool shown)
5661d6c7b6cSAlex Wilson{
5671d6c7b6cSAlex Wilson	if (fAncestorsVisible == shown)
5681d6c7b6cSAlex Wilson		return;
5691d6c7b6cSAlex Wilson
5701d6c7b6cSAlex Wilson	fAncestorsVisible = shown;
5711d6c7b6cSAlex Wilson	VisibilityChanged(shown);
5721d6c7b6cSAlex Wilson}
5731d6c7b6cSAlex Wilson
5741d6c7b6cSAlex Wilson
5751d6c7b6cSAlex Wilsonvoid
5761d6c7b6cSAlex WilsonBLayout::VisibilityChanged(bool show)
5771d6c7b6cSAlex Wilson{
5781d6c7b6cSAlex Wilson	if (fOwner)
5791d6c7b6cSAlex Wilson		return;
5801d6c7b6cSAlex Wilson
5811d6c7b6cSAlex Wilson	for (int32 i = CountItems() - 1; i >= 0; i--)
5821d6c7b6cSAlex Wilson		ItemAt(i)->AncestorVisibilityChanged(show);
5831d6c7b6cSAlex Wilson}
5841d6c7b6cSAlex Wilson
5851d6c7b6cSAlex Wilson
5861d6c7b6cSAlex Wilsonvoid
5871d6c7b6cSAlex WilsonBLayout::ResetLayoutInvalidation()
5881d6c7b6cSAlex Wilson{
5891d6c7b6cSAlex Wilson	fState &= ~B_LAYOUT_CACHE_INVALID;
5901d6c7b6cSAlex Wilson}
5911d6c7b6cSAlex Wilson
5921d6c7b6cSAlex Wilson
5931d6c7b6cSAlex WilsonBLayoutContext*
594a9e89a73SIngo WeinholdBLayout::LayoutContext() const
5951d6c7b6cSAlex Wilson{
5961d6c7b6cSAlex Wilson	return fContext;
5971d6c7b6cSAlex Wilson}
5981d6c7b6cSAlex Wilson
5991d6c7b6cSAlex Wilson
6001d6c7b6cSAlex Wilsonvoid
6011d6c7b6cSAlex WilsonBLayout::SetOwner(BView* owner)
6021d6c7b6cSAlex Wilson{
6031d6c7b6cSAlex Wilson	if (fOwner == owner)
6041d6c7b6cSAlex Wilson		return;
6051d6c7b6cSAlex Wilson
6061d6c7b6cSAlex Wilson	SetTarget(owner);
6071d6c7b6cSAlex Wilson	swap(fOwner, owner);
6081d6c7b6cSAlex Wilson
6091d6c7b6cSAlex Wilson	OwnerChanged(owner);
6101d6c7b6cSAlex Wilson		// call hook
6111d6c7b6cSAlex Wilson}
6121d6c7b6cSAlex Wilson
6131d6c7b6cSAlex Wilson
6141d6c7b6cSAlex Wilsonvoid
6151d6c7b6cSAlex WilsonBLayout::SetTarget(BView* target)
6169ecf9d1cSIngo Weinhold{
6171d6c7b6cSAlex Wilson	if (fTarget != target) {
618cc1ca167SAlex Wilson		/* With fTarget NULL, RemoveItem() will not remove the views from their
619cc1ca167SAlex Wilson		 * parent. This ensures that the views are not lost to the void.
620cc1ca167SAlex Wilson		 */
6211d6c7b6cSAlex Wilson		fTarget = NULL;
6229ecf9d1cSIngo Weinhold
6239ecf9d1cSIngo Weinhold		// remove and delete all items
6249ecf9d1cSIngo Weinhold		for (int32 i = CountItems() - 1; i >= 0; i--)
6259ecf9d1cSIngo Weinhold			delete RemoveItem(i);
6269ecf9d1cSIngo Weinhold
6271d6c7b6cSAlex Wilson		fTarget = target;
6289ecf9d1cSIngo Weinhold
6299ecf9d1cSIngo Weinhold		InvalidateLayout();
6309ecf9d1cSIngo Weinhold	}
6319ecf9d1cSIngo Weinhold}
632fa01d084SAlex Wilson
633a851b3adSAlex Wilson
6348151838eSAlex Wilson// Binary compatibility stuff
6358151838eSAlex Wilson
6368151838eSAlex Wilson
637a851b3adSAlex Wilsonstatus_t
638a851b3adSAlex WilsonBLayout::Perform(perform_code code, void* _data)
639a851b3adSAlex Wilson{
640a851b3adSAlex Wilson	return BLayoutItem::Perform(code, _data);
641a851b3adSAlex Wilson}
642a851b3adSAlex Wilson
6438151838eSAlex Wilson
6448151838eSAlex Wilsonvoid BLayout::_ReservedLayout1() {}
6458151838eSAlex Wilsonvoid BLayout::_ReservedLayout2() {}
6468151838eSAlex Wilsonvoid BLayout::_ReservedLayout3() {}
6478151838eSAlex Wilsonvoid BLayout::_ReservedLayout4() {}
6488151838eSAlex Wilsonvoid BLayout::_ReservedLayout5() {}
6498151838eSAlex Wilsonvoid BLayout::_ReservedLayout6() {}
6508151838eSAlex Wilsonvoid BLayout::_ReservedLayout7() {}
6518151838eSAlex Wilsonvoid BLayout::_ReservedLayout8() {}
6528151838eSAlex Wilsonvoid BLayout::_ReservedLayout9() {}
6538151838eSAlex Wilsonvoid BLayout::_ReservedLayout10() {}
6548151838eSAlex Wilson