vfs: check userland buffers before reading them.
[haiku.git] / src / kits / interface / Layout.cpp
blob4feaa4c9b60619604a753c13153b555b7e27d10c
1 /*
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.
5 */
8 #include <Layout.h>
10 #include <algorithm>
11 #include <new>
12 #include <syslog.h>
14 #include <AutoDeleter.h>
15 #include <LayoutContext.h>
16 #include <Message.h>
17 #include <View.h>
18 #include <ViewPrivate.h>
20 #include "ViewLayoutItem.h"
23 using BPrivate::AutoDeleter;
25 using std::nothrow;
26 using std::swap;
29 namespace {
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";
48 struct ViewRemover {
49 inline void operator()(BView* view) {
50 if (view)
51 BView::Private(view).RemoveSelf();
57 BLayout::BLayout()
59 fState(B_LAYOUT_ALL_CLEAR),
60 fAncestorsVisible(true),
61 fInvalidationDisabled(0),
62 fContext(NULL),
63 fOwner(NULL),
64 fTarget(NULL),
65 fItems(20)
70 BLayout::BLayout(BMessage* from)
72 BLayoutItem(BUnarchiver::PrepareArchive(from)),
73 fState(B_LAYOUT_ALL_CLEAR),
74 fAncestorsVisible(true),
75 fInvalidationDisabled(0),
76 fContext(NULL),
77 fOwner(NULL),
78 fTarget(NULL),
79 fItems(20)
81 BUnarchiver unarchiver(from);
83 int32 i = 0;
84 while (unarchiver.EnsureUnarchived(kLayoutItemField, i++) == B_OK)
89 BLayout::~BLayout()
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");
104 BView*
105 BLayout::Owner() const
107 return fOwner;
111 BView*
112 BLayout::TargetView() const
114 return fTarget;
118 BView*
119 BLayout::View()
121 return fOwner;
125 BLayoutItem*
126 BLayout::AddView(BView* child)
128 return AddView(-1, child);
132 BLayoutItem*
133 BLayout::AddView(int32 index, BView* child)
135 BLayoutItem* item = child->GetLayout();
136 ObjectDeleter<BLayoutItem> itemDeleter(NULL);
137 if (!item) {
138 item = new(nothrow) BViewLayoutItem(child);
139 itemDeleter.SetTo(item);
142 if (item && AddItem(index, item)) {
143 itemDeleter.Detach();
144 return item;
147 return NULL;
151 bool
152 BLayout::AddItem(BLayoutItem* item)
154 return AddItem(-1, item);
158 bool
159 BLayout::AddItem(int32 index, BLayoutItem* item)
161 if (!fTarget || !item || fItems.HasItem(item))
162 return false;
164 // if the item refers to a BView, we make sure it is added to the parent
165 // view
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
169 // shouldn't be.
170 if (view && view->fParent != fTarget) {
171 if (!fTarget->_AddChild(view, NULL))
172 return false;
173 else
174 remover.SetTo(view);
177 // validate the index
178 if (index < 0 || index > fItems.CountItems())
179 index = fItems.CountItems();
181 if (!fItems.AddItem(item, index))
182 return false;
184 if (!ItemAdded(item, index)) {
185 fItems.RemoveItem(index);
186 return false;
189 item->SetLayout(this);
190 if (!fAncestorsVisible)
191 item->AncestorVisibilityChanged(fAncestorsVisible);
192 InvalidateLayout();
193 remover.Detach();
194 return true;
198 bool
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)
209 continue;
211 RemoveItem(i);
212 if (item != child->GetLayout())
213 delete item;
215 remaining--;
216 removed = true;
219 return removed;
223 bool
224 BLayout::RemoveItem(BLayoutItem* item)
226 int32 index = IndexOfItem(item);
227 return (index >= 0 ? RemoveItem(index) != NULL : false);
231 BLayoutItem*
232 BLayout::RemoveItem(int32 index)
234 if (index < 0 || index >= fItems.CountItems())
235 return NULL;
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)
246 view->_RemoveSelf();
248 InvalidateLayout();
249 return item;
253 BLayoutItem*
254 BLayout::ItemAt(int32 index) const
256 return (BLayoutItem*)fItems.ItemAt(index);
260 int32
261 BLayout::CountItems() const
263 return fItems.CountItems();
267 int32
268 BLayout::IndexOfItem(const BLayoutItem* item) const
270 return fItems.IndexOf(item);
274 int32
275 BLayout::IndexOfView(BView* child) const
277 if (child == NULL)
278 return -1;
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);
289 return -1;
293 bool
294 BLayout::AncestorsVisible() const
296 return fAncestorsVisible;
300 void
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())
307 return;
308 if (fInvalidationDisabled > 0
309 || (fState & B_LAYOUT_INVALIDATION_ILLEGAL) != 0) {
310 return;
313 fState |= B_LAYOUT_NECESSARY;
314 LayoutInvalidated(children);
316 if (children) {
317 for (int32 i = CountItems() - 1; i >= 0; i--)
318 ItemAt(i)->InvalidateLayout(children);
321 if (fOwner)
322 fOwner->InvalidateLayout(children);
324 if (BLayout* nestedIn = Layout()) {
325 nestedIn->InvalidateLayout();
326 } else if (fOwner) {
327 // If we weren't added as a BLayoutItem, we still have to invalidate
328 // whatever layout our owner is in.
329 fOwner->_InvalidateParentLayout();
334 void
335 BLayout::RequireLayout()
337 fState |= B_LAYOUT_REQUIRED;
341 bool
342 BLayout::IsValid()
344 return (fState & B_LAYOUT_INVALID) == 0;
348 void
349 BLayout::DisableLayoutInvalidation()
351 fInvalidationDisabled++;
355 void
356 BLayout::EnableLayoutInvalidation()
358 if (fInvalidationDisabled > 0)
359 fInvalidationDisabled--;
363 void
364 BLayout::LayoutItems(bool force)
366 if ((fState & B_LAYOUT_NECESSARY) == 0 && !force)
367 return;
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())
373 return;
375 BLayoutContext context;
376 _LayoutWithinContext(force, &context);
380 void
381 BLayout::Relayout(bool immediate)
383 if ((fState & B_RELAYOUT_NOT_OK) == 0 || immediate) {
384 fState |= B_LAYOUT_REQUIRED;
385 LayoutItems(false);
390 void
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)
397 return;
399 BLayoutContext* oldContext = fContext;
400 fContext = context;
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);
406 } else {
407 fState |= B_LAYOUT_IN_PROGRESS;
408 DoLayout();
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;
426 BRect
427 BLayout::LayoutArea()
429 BRect area(Frame());
430 if (fOwner)
431 area.OffsetTo(B_ORIGIN);
432 return area;
436 status_t
437 BLayout::Archive(BMessage* into, bool deep) const
439 BArchiver archiver(into);
440 status_t err = BLayoutItem::Archive(into, deep);
442 if (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);
448 if (err == B_OK) {
449 err = ItemArchived(into, item, i);
450 if (err != B_OK)
451 syslog(LOG_ERR, "ItemArchived() failed at index: %d.", i);
456 return archiver.Finish(err);
460 status_t
461 BLayout::AllArchived(BMessage* archive) const
463 return BLayoutItem::AllArchived(archive);
467 status_t
468 BLayout::AllUnarchived(const BMessage* from)
470 BUnarchiver unarchiver(from);
471 status_t err = BLayoutItem::AllUnarchived(from);
472 if (err != B_OK)
473 return err;
475 int32 itemCount = 0;
476 unarchiver.ArchiveMessage()->GetInfo(kLayoutItemField, NULL, &itemCount);
477 for (int32 i = 0; i < itemCount && err == B_OK; i++) {
478 BLayoutItem* item;
479 err = unarchiver.FindObject(kLayoutItemField,
480 i, BUnarchiver::B_DONT_ASSUME_OWNERSHIP, item);
481 if (err != B_OK)
482 return err;
484 if (!fItems.AddItem(item, i) || !ItemAdded(item, i)) {
485 fItems.RemoveItem(i);
486 return B_ERROR;
489 err = ItemUnarchived(from, item, i);
490 if (err != B_OK) {
491 fItems.RemoveItem(i);
492 ItemRemoved(item, i);
493 return err;
496 item->SetLayout(this);
497 unarchiver.AssumeOwnership(item);
500 InvalidateLayout();
501 return err;
505 status_t
506 BLayout::ItemArchived(BMessage* into, BLayoutItem* item, int32 index) const
508 return B_OK;
512 status_t
513 BLayout::ItemUnarchived(const BMessage* from, BLayoutItem* item, int32 index)
515 return B_OK;
519 bool
520 BLayout::ItemAdded(BLayoutItem* item, int32 atIndex)
522 return true;
526 void
527 BLayout::ItemRemoved(BLayoutItem* item, int32 fromIndex)
532 void
533 BLayout::LayoutInvalidated(bool children)
538 void
539 BLayout::OwnerChanged(BView* was)
544 void
545 BLayout::AttachedToLayout()
547 if (!fOwner) {
548 Layout()->fNestedLayouts.AddItem(this);
549 SetTarget(Layout()->TargetView());
554 void
555 BLayout::DetachedFromLayout(BLayout* from)
557 if (!fOwner) {
558 from->fNestedLayouts.RemoveItem(this);
559 SetTarget(NULL);
564 void
565 BLayout::AncestorVisibilityChanged(bool shown)
567 if (fAncestorsVisible == shown)
568 return;
570 fAncestorsVisible = shown;
571 VisibilityChanged(shown);
575 void
576 BLayout::VisibilityChanged(bool show)
578 if (fOwner)
579 return;
581 for (int32 i = CountItems() - 1; i >= 0; i--)
582 ItemAt(i)->AncestorVisibilityChanged(show);
586 void
587 BLayout::ResetLayoutInvalidation()
589 fState &= ~B_LAYOUT_CACHE_INVALID;
593 BLayoutContext*
594 BLayout::LayoutContext() const
596 return fContext;
600 void
601 BLayout::SetOwner(BView* owner)
603 if (fOwner == owner)
604 return;
606 SetTarget(owner);
607 swap(fOwner, owner);
609 OwnerChanged(owner);
610 // call hook
614 void
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.
621 fTarget = NULL;
623 // remove and delete all items
624 for (int32 i = CountItems() - 1; i >= 0; i--)
625 delete RemoveItem(i);
627 fTarget = target;
629 InvalidateLayout();
634 // Binary compatibility stuff
637 status_t
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() {}