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 */ 6 7 8 #include <Layout.h> 9 10 #include <algorithm> 11 #include <new> 12 #include <syslog.h> 13 14 #include <LayoutContext.h> 15 #include <Message.h> 16 #include <View.h> 17 #include <ViewPrivate.h> 18 19 #include "ViewLayoutItem.h" 20 21 22 using std::nothrow; 23 using std::swap; 24 25 26 namespace { 27 // flags for our state 28 const uint32 B_LAYOUT_INVALID = 0x80000000UL; // needs layout 29 const uint32 B_LAYOUT_CACHE_INVALID = 0x40000000UL; // needs recalculation 30 const uint32 B_LAYOUT_REQUIRED = 0x20000000UL; // needs layout 31 const uint32 B_LAYOUT_IN_PROGRESS = 0x10000000UL; 32 const uint32 B_LAYOUT_ALL_CLEAR = 0UL; 33 34 // handy masks to check various states 35 const uint32 B_LAYOUT_INVALIDATION_ILLEGAL 36 = B_LAYOUT_CACHE_INVALID | B_LAYOUT_IN_PROGRESS; 37 const uint32 B_LAYOUT_NECESSARY 38 = B_LAYOUT_INVALID | B_LAYOUT_REQUIRED | B_LAYOUT_CACHE_INVALID; 39 const uint32 B_RELAYOUT_NOT_OK 40 = B_LAYOUT_INVALID | B_LAYOUT_IN_PROGRESS; 41 42 const char* const kLayoutItemField = "BLayout:items"; 43 } 44 45 46 BLayout::BLayout() 47 : 48 fState(B_LAYOUT_ALL_CLEAR), 49 fAncestorsVisible(true), 50 fInvalidationDisabled(0), 51 fContext(NULL), 52 fOwner(NULL), 53 fTarget(NULL), 54 fItems(20) 55 { 56 } 57 58 59 BLayout::BLayout(BMessage* from) 60 : 61 BLayoutItem(BUnarchiver::PrepareArchive(from)), 62 fState(B_LAYOUT_ALL_CLEAR), 63 fAncestorsVisible(true), 64 fInvalidationDisabled(0), 65 fContext(NULL), 66 fOwner(NULL), 67 fTarget(NULL), 68 fItems(20) 69 { 70 BUnarchiver unarchiver(from); 71 72 int32 i = 0; 73 while (unarchiver.EnsureUnarchived(kLayoutItemField, i++) == B_OK) 74 ; 75 } 76 77 78 BLayout::~BLayout() 79 { 80 // in case we have a view, but have been added to a layout as a BLayoutItem 81 // we will get deleted before our view, so we should tell it that we're 82 // going, so that we aren't double-freed. 83 if (fOwner && this == fOwner->GetLayout()) 84 fOwner->_LayoutLeft(this); 85 86 // removes and deletes all items 87 if (fTarget) 88 SetTarget(NULL); 89 } 90 91 92 BView* 93 BLayout::Owner() const 94 { 95 return fOwner; 96 } 97 98 99 BView* 100 BLayout::TargetView() const 101 { 102 return fTarget; 103 } 104 105 106 BView* 107 BLayout::View() 108 { 109 return fOwner; 110 } 111 112 113 BLayoutItem* 114 BLayout::AddView(BView* child) 115 { 116 return AddView(-1, child); 117 } 118 119 120 BLayoutItem* 121 BLayout::AddView(int32 index, BView* child) 122 { 123 BLayoutItem* item = child->GetLayout(); 124 if (!item) 125 item = new(nothrow) BViewLayoutItem(child); 126 127 if (item && AddItem(index, item)) 128 return item; 129 130 if (!child->GetLayout()) 131 delete item; 132 return NULL; 133 } 134 135 136 bool 137 BLayout::AddItem(BLayoutItem* item) 138 { 139 return AddItem(-1, item); 140 } 141 142 143 bool 144 BLayout::AddItem(int32 index, BLayoutItem* item) 145 { 146 if (!fTarget || !item || fItems.HasItem(item)) 147 return false; 148 149 // if the item refers to a BView, we make sure it is added to the parent 150 // view 151 bool addedView = false; 152 BView* view = item->View(); 153 if (view && view->fParent != fTarget 154 && !(addedView = fTarget->_AddChild(view, NULL))) 155 return false; 156 157 // validate the index 158 if (index < 0 || index > fItems.CountItems()) 159 index = fItems.CountItems(); 160 161 if (fItems.AddItem(item, index) && ItemAdded(item, index)) { 162 item->SetLayout(this); 163 if (!fAncestorsVisible) 164 item->AncestorVisibilityChanged(fAncestorsVisible); 165 InvalidateLayout(); 166 return true; 167 } else { 168 // this check is necessary so that if an addition somewhere other 169 // than the end of the list fails, we don't remove the wrong item 170 if (fItems.ItemAt(index) == item) 171 fItems.RemoveItem(index); 172 if (addedView) 173 view->_RemoveSelf(); 174 return false; 175 } 176 } 177 178 179 bool 180 BLayout::RemoveView(BView* child) 181 { 182 bool removed = false; 183 184 // a view can have any number of layout items - we need to remove them all 185 for (int32 i = fItems.CountItems(); i-- > 0;) { 186 BLayoutItem* item = ItemAt(i); 187 188 if (item->View() != child) 189 continue; 190 191 RemoveItem(i); 192 removed = true; 193 delete item; 194 } 195 196 return removed; 197 } 198 199 200 bool 201 BLayout::RemoveItem(BLayoutItem* item) 202 { 203 int32 index = IndexOfItem(item); 204 return (index >= 0 ? RemoveItem(index) : false); 205 } 206 207 208 BLayoutItem* 209 BLayout::RemoveItem(int32 index) 210 { 211 if (index < 0 || index >= fItems.CountItems()) 212 return NULL; 213 214 BLayoutItem* item = (BLayoutItem*)fItems.RemoveItem(index); 215 216 // if the item refers to a BView, we make sure, it is removed from the 217 // parent view 218 BView* view = item->View(); 219 if (view && view->fParent == fTarget) 220 view->_RemoveSelf(); 221 222 ItemRemoved(item, index); 223 item->SetLayout(NULL); 224 InvalidateLayout(); 225 226 return item; 227 } 228 229 230 BLayoutItem* 231 BLayout::ItemAt(int32 index) const 232 { 233 return (BLayoutItem*)fItems.ItemAt(index); 234 } 235 236 237 int32 238 BLayout::CountItems() const 239 { 240 return fItems.CountItems(); 241 } 242 243 244 int32 245 BLayout::IndexOfItem(const BLayoutItem* item) const 246 { 247 return fItems.IndexOf(item); 248 } 249 250 251 int32 252 BLayout::IndexOfView(BView* child) const 253 { 254 if (child == NULL) 255 return -1; 256 257 int itemCount = fItems.CountItems(); 258 for (int32 i = 0; i < itemCount; i++) { 259 BLayoutItem* item = (BLayoutItem*)fItems.ItemAt(i); 260 if (item->View() == child && (dynamic_cast<BViewLayoutItem*>(item) 261 || dynamic_cast<BLayout*>(item))) 262 return i; 263 } 264 265 return -1; 266 } 267 268 269 bool 270 BLayout::AncestorsVisible() const 271 { 272 return fAncestorsVisible; 273 } 274 275 276 void 277 BLayout::InvalidateLayout(bool children) 278 { 279 // printf("BLayout(%p)::InvalidateLayout(%i) : state %x, disabled %li\n", 280 // this, children, (unsigned int)fState, fInvalidationDisabled); 281 282 if (!InvalidationLegal()) 283 return; 284 285 fState |= B_LAYOUT_NECESSARY; 286 287 if (children) { 288 for (int32 i = CountItems() - 1; i >= 0; i--) 289 ItemAt(i)->InvalidateLayout(children); 290 } 291 292 if (fOwner && BView::Private(fOwner).MinMaxValid()) 293 fOwner->InvalidateLayout(children); 294 295 if (BLayout* nestedIn = Layout()) { 296 if (nestedIn->InvalidationLegal()) 297 nestedIn->InvalidateLayout(); 298 } else if (fOwner) { 299 // If we weren't added as a BLayoutItem, we still have to invalidate 300 // whatever layout our owner is in. 301 BView* ownerParent = fOwner->fParent; 302 if (ownerParent) { 303 BLayout* layout = ownerParent->GetLayout(); 304 if (layout && layout->fNestedLayouts.CountItems() > 0) 305 layout->InvalidateLayoutsForView(fOwner); 306 else if (BView::Private(ownerParent).MinMaxValid()) 307 ownerParent->InvalidateLayout(false); 308 } 309 } 310 } 311 312 313 void 314 BLayout::RequireLayout() 315 { 316 fState |= B_LAYOUT_REQUIRED; 317 } 318 319 320 bool 321 BLayout::IsValid() 322 { 323 return (fState & B_LAYOUT_INVALID) == 0; 324 } 325 326 327 void 328 BLayout::DisableLayoutInvalidation() 329 { 330 fInvalidationDisabled++; 331 } 332 333 334 void 335 BLayout::EnableLayoutInvalidation() 336 { 337 if (fInvalidationDisabled > 0) 338 fInvalidationDisabled--; 339 } 340 341 342 void 343 BLayout::LayoutItems(bool force) 344 { 345 if ((fState & B_LAYOUT_NECESSARY) == 0 && !force) 346 return; 347 348 if (Layout() && (Layout()->fState & B_LAYOUT_IN_PROGRESS) != 0) 349 return; // wait for parent layout to lay us out. 350 351 if (fTarget && fTarget->LayoutContext()) 352 return; 353 354 BLayoutContext context; 355 _LayoutWithinContext(force, &context); 356 } 357 358 359 void 360 BLayout::Relayout(bool immediate) 361 { 362 if ((fState & B_RELAYOUT_NOT_OK) == 0 || immediate) { 363 fState |= B_LAYOUT_REQUIRED; 364 LayoutItems(false); 365 } 366 } 367 368 369 370 void 371 BLayout::_LayoutWithinContext(bool force, BLayoutContext* context) 372 { 373 // printf("BLayout(%p)::_LayoutWithinContext(%i, %p), state %x, fContext %p\n", 374 // this, force, context, (unsigned int)fState, fContext); 375 376 if ((fState & B_LAYOUT_NECESSARY) == 0 && !force) 377 return; 378 379 BLayoutContext* oldContext = fContext; 380 fContext = context; 381 382 if (fOwner && BView::Private(fOwner).WillLayout()) { 383 // in this case, let our owner decide whether or not to have us 384 // do our layout, if they do, we won't end up here again. 385 fOwner->_Layout(force, context); 386 } else { 387 fState |= B_LAYOUT_IN_PROGRESS; 388 DerivedLayoutItems(); 389 // we must ensure that all items are laid out, layouts with a view will 390 // have their layout process triggered by their view, but nested 391 // view-less layouts must have their layout triggered here (if it hasn't 392 // already been triggered). 393 int32 nestedLayoutCount = fNestedLayouts.CountItems(); 394 for (int32 i = 0; i < nestedLayoutCount; i++) { 395 BLayout* layout = (BLayout*)fNestedLayouts.ItemAt(i); 396 if ((layout->fState & B_LAYOUT_NECESSARY) != 0) 397 layout->_LayoutWithinContext(force, context); 398 } 399 fState = B_LAYOUT_ALL_CLEAR; 400 } 401 402 fContext = oldContext; 403 } 404 405 406 BRect 407 BLayout::LayoutArea() 408 { 409 BRect area(Frame()); 410 if (fOwner) 411 area.OffsetTo(B_ORIGIN); 412 return area; 413 } 414 415 416 status_t 417 BLayout::Archive(BMessage* into, bool deep) const 418 { 419 BArchiver archiver(into); 420 status_t err = BLayoutItem::Archive(into, deep); 421 422 if (deep) { 423 int32 count = CountItems(); 424 for (int32 i = 0; i < count && err == B_OK; i++) { 425 BLayoutItem* item = ItemAt(i); 426 err = archiver.AddArchivable(kLayoutItemField, item, deep); 427 428 if (err == B_OK) { 429 err = ItemArchived(into, item, i); 430 if (err != B_OK) 431 syslog(LOG_ERR, "ItemArchived() failed at index: %d.", i); 432 } 433 } 434 } 435 436 return archiver.Finish(err); 437 } 438 439 440 status_t 441 BLayout::AllUnarchived(const BMessage* from) 442 { 443 BUnarchiver unarchiver(from); 444 status_t err = BLayoutItem::AllUnarchived(from); 445 if (err != B_OK) 446 return err; 447 448 int32 itemCount; 449 unarchiver.ArchiveMessage()->GetInfo(kLayoutItemField, NULL, &itemCount); 450 for (int32 i = 0; i < itemCount && err == B_OK; i++) { 451 BLayoutItem* item; 452 err = unarchiver.FindObject(kLayoutItemField, 453 i, BUnarchiver::B_DONT_ASSUME_OWNERSHIP, item); 454 if (err != B_OK) 455 return err; 456 457 if (!fItems.AddItem(item, i) || !ItemAdded(item, i)) { 458 fItems.RemoveItem(i); 459 return B_ERROR; 460 } 461 462 err = ItemUnarchived(from, item, i); 463 if (err != B_OK) { 464 fItems.RemoveItem(i); 465 ItemRemoved(item, i); 466 return err; 467 } 468 469 item->SetLayout(this); 470 unarchiver.AssumeOwnership(item); 471 } 472 473 InvalidateLayout(); 474 return err; 475 } 476 477 478 status_t 479 BLayout::ItemArchived(BMessage* into, BLayoutItem* item, int32 index) const 480 { 481 return B_OK; 482 } 483 484 485 status_t 486 BLayout::ItemUnarchived(const BMessage* from, BLayoutItem* item, int32 index) 487 { 488 return B_OK; 489 } 490 491 492 bool 493 BLayout::ItemAdded(BLayoutItem* item, int32 atIndex) 494 { 495 return true; 496 } 497 498 499 void 500 BLayout::ItemRemoved(BLayoutItem* item, int32 fromIndex) 501 { 502 } 503 504 505 void 506 BLayout::OwnerChanged(BView* was) 507 { 508 } 509 510 511 void 512 BLayout::AttachedToLayout() 513 { 514 if (!fOwner) { 515 Layout()->fNestedLayouts.AddItem(this); 516 SetTarget(Layout()->TargetView()); 517 } 518 } 519 520 521 void 522 BLayout::DetachedFromLayout(BLayout* from) 523 { 524 if (!fOwner) { 525 from->fNestedLayouts.RemoveItem(this); 526 SetTarget(NULL); 527 } 528 } 529 530 531 void 532 BLayout::AncestorVisibilityChanged(bool shown) 533 { 534 if (fAncestorsVisible == shown) 535 return; 536 537 fAncestorsVisible = shown; 538 VisibilityChanged(shown); 539 } 540 541 542 void 543 BLayout::VisibilityChanged(bool show) 544 { 545 if (fOwner) 546 return; 547 548 for (int32 i = CountItems() - 1; i >= 0; i--) 549 ItemAt(i)->AncestorVisibilityChanged(show); 550 } 551 552 553 void 554 BLayout::ResetLayoutInvalidation() 555 { 556 fState &= ~B_LAYOUT_CACHE_INVALID; 557 } 558 559 560 BLayoutContext* 561 BLayout::LayoutContext() const 562 { 563 return fContext; 564 } 565 566 567 bool 568 BLayout::RemoveViewRecursive(BView* view) 569 { 570 bool removed = RemoveView(view); 571 for (int32 i = fNestedLayouts.CountItems() - 1; i >= 0; i--) { 572 BLayout* nested = (BLayout*)fNestedLayouts.ItemAt(i); 573 removed |= nested->RemoveViewRecursive(view); 574 } 575 return removed; 576 } 577 578 579 bool 580 BLayout::InvalidateLayoutsForView(BView* view) 581 { 582 bool found = false; 583 for (int32 i = fNestedLayouts.CountItems() - 1; i >= 0; i--) { 584 BLayout* layout = (BLayout*)fNestedLayouts.ItemAt(i); 585 found |= layout->InvalidateLayoutsForView(view); 586 } 587 588 if (found) 589 return found; 590 591 if (!InvalidationLegal()) 592 return false; 593 594 for (int32 i = CountItems() - 1; i >= 0; i--) { 595 if (ItemAt(i)->View() == view) { 596 InvalidateLayout(); 597 return true; 598 } 599 } 600 return found; 601 } 602 603 604 bool 605 BLayout::InvalidationLegal() 606 { 607 return fInvalidationDisabled <= 0 608 && (fState & B_LAYOUT_INVALIDATION_ILLEGAL) == 0; 609 } 610 611 612 void 613 BLayout::SetOwner(BView* owner) 614 { 615 if (fOwner == owner) 616 return; 617 618 SetTarget(owner); 619 swap(fOwner, owner); 620 621 OwnerChanged(owner); 622 // call hook 623 } 624 625 626 void 627 BLayout::SetTarget(BView* target) 628 { 629 if (fTarget != target) { 630 fTarget = NULL; 631 // only remove items, not views 632 633 // remove and delete all items 634 for (int32 i = CountItems() - 1; i >= 0; i--) 635 delete RemoveItem(i); 636 637 fTarget = target; 638 639 InvalidateLayout(); 640 } 641 } 642